@[toc]java
是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。面试
- 进程做为资源分配的单位,系统在运行时会为每一个进程分配不一样的内存区域
- 程序是静态的,进程是动态的
进程可进一步细化为线程,是一个程序内部的一条执行路径算法
- 若一个进程同一时间 并行执行多个线程,就是支持多线程的
- 线程做为调度和执行的单位,每一个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小
- 一个进程中的多个线程共享相同的内存单元/内存地址空间---->它们从同一堆中分配对象,能够访问相同的变量和对象。这就使得线程间通讯更简便、高效。但多个线程操做共享的系统资源可能就会带来安全的隐患。
区别:设计模式
并行:多个CPU同时执行多个任务。好比:多我的同时作不一样的事。安全
并发:一个CPU(采用时间片)同时执行多个任务。好比:秒杀、多我的作同一件事。多线程
Thread()
:建立新的Thread对象Thread(String threadname)
:建立线程并指定线程实例名Thread(Runnable target)
:指定建立线程的目标对象,它实现了Runnable接口中的run方法Thread(Runnable target, String name)
:建立新的Thread对象JDK1.5以前建立新执行线程有两种方法:并发
继承Thread类的方式app
实现Runnable接口的方式ide
继承Thread类工具
定义子类继承Thread类。
子类中重写Thread类中的run方法。
建立Thread子类对象,即建立了线程对象。
调用线程对象start方法:启动线程,调用run方法。
mt子线程的建立和启动过程
IllegalThreadStateException
建立线程代码以下:
package com.shsxt.thread;
/** * 多线程的建立,方式一:继承于Thread类 */
//1. 建立一个继承于Thread类的子类
class MyThread extends Thread {
//2. 重写Thread类的run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest {
public static void main(String[] args) {
//3. 建立Thread类的子类的对象
MyThread mt = new MyThread();
//4.经过此对象调用start():①启动当前线程 ② 调用当前线程的run()
mt.start();
//问题一:咱们不能经过直接调用run()的方式启动线程。
//mt.run();
//问题二:再启动一个线程,遍历100之内的偶数。不能够还让已经start()的线程去执行。会报IllegalThreadStateException
//t1.start();
//咱们须要从新建立一个线程的对象
MyThread mt2 = new MyThread();
mt2.start();
//以下操做仍然是在main线程中执行的。
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i + "******main*******");
}
}
}
}
复制代码
public class ThreadDemo {
public static void main(String[] args) {
//建立Thread类的匿名子类的方式
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100 ; i++) {
if (i%2==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if(i % 2 != 0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}.start();
}
}
复制代码
void start()
: 启动线程,并执行对象的run()方法
run()
: 线程在被调度时执行的操做
String getName()
: 返回线程的名称
void setName(String name)
:设置该线程名称
static Thread currentThread()
: 返回当前线程。在Thread子类中就是this,一般用于主线程和Runnable实现类
static void yield()
:线程让步
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程
join()
:在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b彻底执行完之后,线程a才结束阻塞状态。
static void sleep(long millis)
:(指定时间:毫秒);让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前线程是阻塞状态。
boolean isAlive()
:返回boolean,判断线程是否还活着
调度策略
- 时间片:
![]()
- 抢占式: 高优先级的线程抢占CPU
Java的调度方法
- 同优先级线程组成先进先出队列(先到先服务),使用时间片策略
- 对高优先级,使用优先调度的抢占式策略
线程的优先级等级:
MAX_PRIORITY :10 MIN _PRIORITY :1 NORM_PRIORITY :5
涉及的方法
getPriority() :返回线程优先值 setPriority(int newPriority) :改变线程的优先级
注意点:
线程建立时继承父线程的优先级
低优先级只是得到调度的几率低,并不是必定是在高优先级线程以后才被调用
关于方法的一些使用,代码以下:
package com.shsxt.thread;
class HelloThread extends Thread {
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":" + getPriority() + ":" + i);
}
//yield()方法的使用(礼让线程)
// if (i % 20 == 0) {
// yield();
// }
}
}
public HelloThread(String name) {
//给子线程赋名字
super(name);
}
}
public class ThreadMethod {
public static void main(String[] args) {
//第一种方式:给子线程赋名字
HelloThread h1 = new HelloThread("Thread:1");
//第二种方式:给子线程赋名字
//h1.setName("线程一");
//给子线程设置优先级
//h1.setPriority(Thread.MAX_PRIORITY);
//启动子线程
h1.start();
//给主线程命名
Thread.currentThread().setName("主线程");
//给主线程设置优先级
//Thread.currentThread().setPriority(Thread.MIN_PRIORITY);
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + Thread.currentThread().getPriority() + ":" + i);
}
//调用join()方法,当i==20时主线程阻塞,子线程运行完后,主线程才运行
if (i == 20){
try {
h1.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//判断线程是否还存活着
System.out.println(h1.isAlive());
}
}
复制代码
使用继承Thread类,写一个窗口卖票的练习
class Window extends Thread{
//使用static关键字是防止new Window()每一个线程都有100张票
//不使用static关键字的话,须要用到建立线程的第二种方式实现Runnable接口
private static int tickets = 100;
@Override
public void run() {
while (true){
if (tickets>0){
System.out.println(getName()+":卖票,票号为"+tickets);
tickets--;
}else {
break;
}
}
}
}
public class WindowTest {
public static void main(String[] args) {
Window w = new Window();
Window w1 = new Window();
Window w2 = new Window();
w.setName("窗口一");
w1.setName("窗口二");
w2.setName("窗口三");
w.start();
w1.start();
w2.start();
}
}
复制代码
实现Runnable接口
- 建立一个实现了Runnable接口的类
- 实现类去实现Runnable中的抽象方法:run()
- 建立实现类的对象
- 将此对象做为参数传递到Thread类的构造器中,建立Thread类的对象
- 经过Thread类的对象调用start()
线程建立,代码以下:
package com.shsxt.thread;
//1. 建立一个实现了Runnable接口的类
class MyThread1 implements Runnable {
//二、实现类去实现Runnable中的抽象方法:run()
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
}
public class ThreadTest1 {
public static void main(String[] args) {
//3. 建立实现类的对象
MyThread1 myThread1 = new MyThread1();
//4. 将此对象做为参数传递到Thread类的构造器中,建立Thread类的对象
Thread t1 = new Thread(myThread1);
t1.setName("线程1");
//5. 经过Thread类的对象调用start():① 启动线程 ②调用当前线程的run()-->调用了Runnable类型的target的run()
t1.start();
//再启动一个线程,遍历100之内的偶数
Thread t2 = new Thread(myThread1);
t2.setName("线程2");
t2.start();
}
}
复制代码
相同点:
两种方式都须要重写run(),将线程要执行的逻辑声明在run()中。
不一样点:
开发中:优先选择:实现Runnable接口的方式
缘由:一、实现了Runnable接口的方式解决了类的单继承性的局限性
二、实现Runnable接口的方式更适合来处理多个线程有共享数据的状况。
使用继承Thread类,写一个窗口卖票的练习:
package com.shsxt.thread;
/** * 建立三个窗口卖票,总票数为100张.使用实现Runnable接口的方式 * @author Rainbow * @date 2020/7/15 10:31 */
class Window1 implements Runnable {
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + tickets);
tickets--;
} else {
break;
}
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w1 = new Window1();
Thread t1 = new Thread(w1);
Thread t2 = new Thread(w1);
Thread t3 = new Thread(w1);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
复制代码
要想实现多线程,必须在主线程中建立新的线程对象。Java语言使用Thread类及其子类的对象来表示线程,在它的一个完整的生命周期中一般要经历以下的 五种状态:
首先举个例子看下:
package com.shsxt.day;
/** * @author Rainbow * @date 2020/7/15 9:15 */
class Window extends Thread {
private static int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
System.out.println(getName() + ":卖票,票号为" + tickets);
tickets--;
} else {
break;
}
}
}
}
/** * @author Rainbow */
public class WindowTest {
public static void main(String[] args) {
Window w = new Window();
Window w1 = new Window();
Window w2 = new Window();
w.setName("窗口一");
w1.setName("窗口二");
w2.setName("窗口三");
w.start();
w1.start();
w2.start();
}
}
复制代码
由此代码看出,发现有线程安全问题:理想状态下
极端状态:
由上述代码能够看出出现了线程安全问题
问题的缘由:
当多条语句在操做同一个线程共享数据时,一个线程对多条语句只执行了一部分,尚未执行完,另外一个线程参与进来执行。致使共享数据的错误。
解决办法:
对多条操做共享数据的语句,只能让一个线程都执行完,在执行过程当中,其余线程不能够参与执行。即便有条线程发生了阻塞,也不能改变,也得等这条线程执行完毕,其余线程才能执行
Java 对于多线程的安全问题提供了专业的解决方式 : 同步机制
同步代码块:
synchronized(同步监视器){
//须要被同步的代码
}
synchronized 还能够放在方法声明中,表示整个方法为同步方法 。
public synchronized void show (){ …. }
关于以上名词的说明:
同步的代码:操做共享数据的代码 ----------->(同步的范围)同步的代码不能被同步代码块包含多了,也不能包含少了
***同步监视器(俗称:锁)***:任何一个类的对象,均可以充当锁。要求:多个线程必须共同拥有一把锁
同步机制中的锁和注意事项:
一、任意对象均可以做为同步锁。全部对象都自动含有单一的锁(监视器)
二、同步方法的锁:静态方法(类名.class)、非静态方法(this)
三、同步代码块:本身指定,不少时候也是指定为this或类名.class
注意事项:
一、必须确保使用同一个资源的 多个线程共用一把锁,这个很是重要,不然就没法保证共享资源的安全
二、 一个线程类中的全部静态方法共用同一把锁(类名.class),全部非静态方法共用同一把锁(this),同步代码块(指定需谨慎)
使用同步方式的优缺点:
优势:解决了线程的安全问题。
缺点:操做同步代码时,只能有一个线程参与,其余线程等待。至关因而一个单线程的过程,效率低。
package com.shsxt.thread;
/** * 在实现Runnable接口建立多线程的方式中,咱们能够考虑使用this充当同步监视器。 * @author Rainbow * @date 2020/7/15 16:22 */
class Window1 implements Runnable {
private int ticket = 100;
//使用同步代码块的第一种解决方式
Object obj = new Object();
@Override
public void run() {
while (true) {
// synchronized (obj) {
//使用同步代码块的第二种解决方式
synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
}
// }
}
}
}
public class WindowTest1 {
public static void main(String[] args) {
Window1 w = new Window1();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
复制代码
package com.shsxt.thread;
/** * 在继承Thread类建立多线程的方式中,慎用this充当同步监视器,考虑使用当前类充当同步监视器。 * * @author Rainbow * @date 2020/7/15 16:33 */
class Window2 extends Thread {
private static int ticket = 100;
private static Object obj = new Object();
@Override
public void run() {
while (true) {
//正确的方式:
// synchronized (obj) {
synchronized (Window2.class) { //Class clazz = Window2.class,Window2.class只会加载一次
//错误的方式:
// synchronized (this) {//此时的this表明着t1,t2,t3三个对象
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
break;
}
// }
}
// }
}
}
}
public class WindowTest2 {
public static void main(String[] args) {
Window2 t1 = new Window2();
Window2 t2 = new Window2();
Window2 t3 = new Window2();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
复制代码
package com.shsxt.thread;
/** * @author Rainbow * @date 2020/7/15 16:40 */
class Window3 implements Runnable {
private int ticket = 100;
boolean flag = true;
@Override
public void run() {
while (flag) {
show();
}
}
private synchronized void show() {//同步监视器:this
//至关于下面的方式
// synchronized (this) {
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
flag = false;
}
// }
}
}
public class WindowTest3 {
public static void main(String[] args) {
Window3 w = new Window3();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
复制代码
package com.shsxt.thread;
/** * 使用同步方法处理继承Thread类的方式中的线程安全问题 * @author Rainbow * @date 2020/7/15 16:59 */
class Window4 extends Thread {
static boolean flag = true;
private static int ticket = 100;
@Override
public void run() {
while (flag) {
show();
}
}
private static synchronized void show() {//同步监视器:Window4.class
//没有static关键字修饰,此时的同步监视器为:t1,t2,t3;此种解决方式是错误的
// private synchronized void show(){
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
} else {
flag = false;
}
// }
}
}
public class WindowTest4 {
public static void main(String[] args) {
Window4 t1 = new Window4();
Window4 t2 = new Window4();
Window4 t3 = new Window4();
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
复制代码
package com.shsxt.thread;
/** * 单例线程安全的懒汉模式 * @author Rainbow * @date 2020/7/15 17:08 */
class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
//方式1、效率低
// synchronized (Singleton.class) {
// if (instance == null) {
// instance = new Singleton();
// }
// return instance;
// }
//方式2、效率高
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
public class SingletonTest {
public static void main(String[] args) {
Singleton instance = Singleton.getInstance();
Singleton instance1 = Singleton.getInstance();
Singleton instance2 = Singleton.getInstance();
Singleton instance3 = Singleton.getInstance();
System.out.println(instance);
System.out.println(instance1);
System.out.println(instance2);
System.out.println(instance3);
}
}
复制代码
死锁问题的产生
- 不一样的线程分别占用对方须要的同步资源不放弃,都在等待对方放弃本身须要的同步资源,就造成了线程的死锁
- 出现死锁后,不会出现异常,不会出现提示,只是全部的线程都处于阻塞状态,没法继续
解决方法
用专门的算法、原则
尽可能减小同步资源的定义
尽可能避免嵌套同步
案例
package com.shnsxt.thread;
/** * @author Rainbow * @date 2020/7/15 21:28 */
public class ThreadTest {
public static void main(String[] args) {
StringBuffer s1 = new StringBuffer();
StringBuffer s2 = new StringBuffer();
new Thread(){
@Override
public void run() {
synchronized (s1){
s1.append("a");
s2.append("1");
//增大产生死锁的概率
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s2){
s1.append("b");
s2.append("2");
System.out.println(s1);
System.out.println(s2);
}
}
}
}.start();
new Thread(){
@Override
public void run() {
synchronized (s2){
s1.append("c");
s2.append("3");
}
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (s1){
s1.append("d");
s2.append("4");
System.out.println(s1);
System.out.println(s2);
}
}
}.start();
}
}
复制代码
java.util.concurrent.locks.Lock
接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问,每次只能有一个线程对Lock对象加锁,线程开始访问共享资源以前应先得到Lock对象相同点:
两者均可以解决线程安全问题
不一样点:
一、synchronized机制在执行完相应的同步代码之后,自动的释放同步监视器
二、Lock须要手动的启动同步(lock()方法)紧跟try代码块,同时结束同步也须要手动的实现(unlock()方法)且必须放在finally的首行
优先使用顺序
Lock ---> 同步代码块(已经进入了方法体,分配了相应资源)----> 同步方法(在方法体以外)
使用Lock锁的案例:
package com.shnsxt.thread;
import java.util.concurrent.locks.ReentrantLock;
/** * @author Rainbow * @date 2020/7/15 21:41 */
class Window implements Runnable {
private int ticket = 100;
//1.实例化ReentrantLock
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while (true) {
try {
//2.调用锁定方法lock()
lock.lock();
if (ticket > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":售票,票号为:" + ticket);
ticket--;
} else {
break;
}
} finally {
//3.调用解锁方法:unlock(),且必须放在finally的首行
lock.unlock();
}
}
}
}
public class LockTest {
public static void main(String[] args) {
Window w = new Window();
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
Thread t3 = new Thread(w);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
复制代码
package com.shnsxt.thread;
/** * 银行有一个帐户。 * 有两个储户分别向同一个帐户存3000元,每次存1000,存3次。每次存完打印帐户余额。 * @author Rainbow * @date 2020/7/15 21:52 */
class Account{
private double balance;
public Account(double balance) {
this.balance = balance;
}
//存钱
public synchronized void deposit(double AMB) {//同步监视器:this;虽说使用继承Thread类慎用this,可是此处的this不表明Customer,而是Account
if (AMB>0){
balance+=AMB;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":存钱成功。余额为:" + balance);
}
}
}
//储户
class Customer extends Thread{
private Account account;
public Customer(Account account) {
this.account = account;
}
@Override
public void run() {
for (int i = 0; i < 3 ; i++) {
account.deposit(1000);
}
}
}
public class AccountTest {
public static void main(String[] args) {
Account account = new Account(0);
Customer customer = new Customer(account);
Customer customer1 = new Customer(account);
customer.setName("甲");
customer1.setName("已");
customer.start();
customer1.start();
}
}
复制代码
package com.shnsxt.thread;
/** * @author Rainbow * @date 2020/7/16 9:37 */
class Blank {
private String accountId;
private double balance;
public Blank(String accountId, double balance) {
this.accountId = accountId;
this.balance = balance;
}
public double getBalance() {
return balance;
}
public String getAccountId() {
return accountId;
}
public void setAccountId(String accountId) {
this.accountId = accountId;
}
public void setBalance(double balance) {
this.balance = balance;
}
@Override
public String toString() {
return "Blank{" +
"accountId='" + accountId + '\'' +
", balance=" + balance +
'}';
}
}
class DrawThread extends Thread {
private Blank blank;
//取款额度
private double money;
public DrawThread(String name, Blank blank, double money) {
super(name);
this.blank = blank;
this.money = money;
}
@Override
public void run() {
synchronized (blank) {
if (blank.getBalance() > money) {
System.out.println(Thread.currentThread().getName() + ":取款成功," + "取现的金额为" + money);
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
blank.setBalance(blank.getBalance() - money);
} else {
System.out.println("取现额度超过帐户余额,取款失败");
}
}
System.out.println(blank.getAccountId() + "帐户的余额为:" + blank.getBalance());
}
}
public class DrawThreadTest {
public static void main(String[] args) {
Blank blank = new Blank("中国银行", 100000.00);
DrawThread d1 = new DrawThread("张三", blank, 4000);
DrawThread d2 = new DrawThread("李四", blank, 5000);
DrawThread d3 = new DrawThread("王五", blank, 8000);
d1.start();
d2.start();
d3.start();
}
}
复制代码
package com.shnsxt.thread;
/** * @author Rainbow * @date 2020/7/15 22:07 */
class Number implements Runnable {
private int number = 1;
private Object obj = new Object();
@Override
public void run() {
while (true) {
synchronized (obj) {
if (number <= 100) {
//唤醒被wait的一个线程。
obj.notify();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + ":" + number);
number++;
try {
//使得调用以下wait()方法的线程进入阻塞状态
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
} else {
break;
}
}
}
}
}
public class CommunicationTest {
public static void main(String[] args) {
Number number = new Number();
Thread t1 = new Thread(number);
Thread t2 = new Thread(number);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
复制代码
wait()
:一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器(锁)。notify()
:一旦执行此方法,就会唤醒被wait的一个线程,若是有多个线程被wait,就唤醒优先级高的那个。notifyAll()
:一旦执行此方法,就会唤醒全部被wait的线程。wait()
,notify()
,notifyAll()
三个方法必须使用在同步代码块或同步方法中。wait()
,notify()
,notifyAll()
三个方法的调用者必须是同步代码块或同步方法中的同步监视器(锁)。不然,会出现IllegalMonitorStateException异常wait()
,notify()
,notifyAll()
三个方法是定义在java.lang.Object类中。相同点:一旦执行方法,均可以使得当前的线程进入阻塞状态。
不一样点:
一、两个方法声明的位置不一样:Thread类中声明sleep() , Object类中声明wait()
二、调用的要求(范围)不一样:sleep()能够在任何须要的场景下调用,wait()必须使用在同步代码块或同步方法中
三、关因而否释放同步监视器(锁):若是两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。
package com.shnsxt.thread;
/** * 经典例题:生产者/消费者的问题 * * @author Rainbow * @date 2020/7/16 9:07 */
class Clerk {
private int productCount = 0;
/** * 生产产品 */
public synchronized void produceProduct() {
if (productCount < 20) {
productCount++;
System.out.println(Thread.currentThread().getName() + ":开始生产第" + productCount + "产品");
//线程唤醒
this.notify();
} else {
//线程等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
/** * 消费产品 */
public synchronized void consumerProduct() {
if (productCount > 0) {
System.out.println(Thread.currentThread().getName() + ":开始消费第" + productCount + "产品");
productCount--;
//线程唤醒
this.notify();
} else {
//线程等待
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/** * 生产者 */
class Producer extends Thread {
private Clerk clerk;
public Producer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + ": 开始生产产品......");
while (true) {
try {
sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
//生产产品
clerk.produceProduct();
}
}
}
/** * 消费者 */
class Consumer extends Thread {
private Clerk clerk;
public Consumer(Clerk clerk) {
this.clerk = clerk;
}
@Override
public void run() {
System.out.println(getName() + ": 开始消费产品......");
while (true) {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//消费产品
clerk.consumerProduct();
}
}
}
public class ProducetTest {
public static void main(String[] args) {
Clerk clerk = new Clerk();
//生产者
Producer p1 = new Producer(clerk);
p1.setName("生产者");
//消费者
Consumer c1 = new Consumer(clerk);
c1.setName("消费者1");
Consumer c2 = new Consumer(clerk);
c2.setName("消费者2");
p1.start();
c1.start();
c2.start();
}
}
复制代码
与使用Runnable相比, Callable功能更强大些
一、 相比run()方法,能够有返回值
二、 方法能够抛出异常
三、支持泛型的返回值
四、须要借助FutureTask类,好比获取返回结果
Future接口
一、 能够对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。
二、FutrueTask是Futrue接口的惟一的实现类
三、 FutureTask 同时实现了Runnable, Future接口。它既能够做为Runnable被线程执行,又能够做为Future获得Callable的返回值
代码理解,以下所示:
package com.shnsxt.thread;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/** * 建立线程的方式三:实现Callable接口。 --- JDK 5.0新增 * 1. call()能够有返回值的。 * 2. call()能够抛出异常,被外面的操做捕获,获取异常的信息 * 3. Callable是支持泛型的 * * @author Rainbow * @date 2020/7/16 10:31 */
//1.建立一个实现Callable的实现类
class NumThread implements Callable<Integer> {
//2.实现call方法,将此线程须要执行的操做声明在call()方法中
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
if (i % 2 == 0) {
sum += i;
}
}
return sum;
}
}
public class ThreadCallable {
public static void main(String[] args) {
//3.建立Callable接口实现类的对象
NumThread numThread = new NumThread();
//4.将此Callable接口实现类的对象做为传递到FutureTask构造器中,建立FutureTask的对象
FutureTask<Integer> futureTask = new FutureTask(numThread);
//5.将FutureTask的对象做为参数传递到Thread类的构造器中,建立Thread对象,并调用start()
new Thread(futureTask).start();
try {
//6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。
Integer sum = futureTask.get();
System.out.println("总和为:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
复制代码
为何要用线程池?
常常建立和销毁、使用量特别大的资源,好比并发状况下的线程,对性能影响很大。
使用线程池的好处
一、提升响应速度(减小了建立新线程的时间) 二、下降资源消耗(重复利用线程池中线程,不须要每次都建立)
便于线程的管理:
一、
corePoolSize
:核心池的大小二、
maximumPoolSize
:最大线程数三、
keepAliveTime
:线程没有任务时最多保持多长时间后会终止
线程池相关的API
JDK 5.0起提供了线程池相关API:
ExecutorService
和Executors
ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command)
:执行任务/命令,没有返回值,通常用来执行Runnable<T> Future <T> submit(Callable<T> task)
:执行任务,有返回值,通常又来执行Callablevoid shutdown()
:关闭链接池Executors:工具类、线程池的工厂类,用于建立并返回不一样类型的线程池
Executors.newCachedThreadPool()
:建立一个可根据须要建立新线程的线程池,主要的问题是线程数最大数Integer.MAX_VALUE,可能会建立数量很是多的线程Executors.newFixedThreadPool(n)
: 建立一个可重用固定线程数的线程池,主要问题是堆积的请求处理队列可能会损耗很是大的内存Executors.newSingleThreadExecutor()
:建立一个只有一个线程的线程池,主要问题是堆积的请求处理队列可能会损耗很是大的内存Executors.newScheduledThreadPool(n)
:建立一个线程池,它可安排在给定延迟后运行命令或者按期地执行。主要的问题是线程数最大数Integer.MAX_VALUE,可能会建立数量很是多的线程
代码理解,以下所示:
package com.shnsxt.thread;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/** * 建立线程的方式四之二:使用线程池 * 好处: * 1.提升响应速度(减小了建立新线程的时间) * 2.下降资源消耗(重复利用线程池中线程,不须要每次都建立) * 3.便于线程管理 * corePoolSize:核心池的大小 * maximumPoolSize:最大线程数 * keepAliveTime:线程没有任务时最多保持多长时间后会终止 * * @author Rainbow * @date 2020/7/16 11:08 */
class NumberThread implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
class NumberThread1 implements Runnable {
@Override
public void run() {
for (int i = 0; i <= 100; i++) {
if (i % 2 != 0) {
System.out.println(Thread.currentThread().getName() + ": " + i);
}
}
}
}
public class ThreadPool {
public static void main(String[] args) {
//1. 提供指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);//这里可能会出现上面写出的问题
ThreadPoolExecutor pool = (ThreadPoolExecutor) service;
//设置线程池的属性
// pool.setCorePoolSize(15);
// pool.setKeepAliveTime(60,MINUTES);
//2.执行指定的线程的操做。须要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable
service.execute(new NumberThread1());//适合适用于Runnable
// service.submit(Callable callable);//适合使用于Callable
//3.关闭链接池
service.shutdown();
}
}
复制代码
若是,此文章对你有所帮助,请帮忙点个赞呗! 谢谢!!!