<>一、创建线程方式
1、多线程的创建,方式一:继承于Thread类 * 1. 创建一个继承于Thread类的子类 * 2. 重写Thread类的run() -->
将此线程执行的操作声明在run()中 * 3. 创建Thread类的子类的对象 * 4. 通过此对象调用start() 2
、创建多线程的方式二:实现Runnable接口* 1. 创建一个实现了Runnable接口的类 * 2. 实现类去实现Runnable中的抽象方法:run()
* 3. 创建实现类的对象 * 4. 将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 * 5. 通过Thread类的对象调用start
() * * * 比较创建线程的两种方式。 * 开发中:优先选择:实现Runnable接口的方式 * 原因:1. 实现的方式没有类的单继承性的局限性 * 2.
实现的方式更适合来处理多个线程有共享数据的情况。* * 联系:public class Thread implements Runnable *
相同点:两种方式都需要重写run(),将线程要执行的逻辑声明在run()中。 * 3、创建线程的方式三:实现Callable接口。 --- JDK 5.0新增
* 1.创建一个实现Callable的实现类 * 2.实现call方法,将此线程需要执行的操作声明在call()中 * 3.创建Callable接口实现类的对象
* 4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 * 5.
将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() * 6.
获取Callable中call方法的返回值/** * 创建线程的方式三:实现Callable接口。 --- JDK 5.0新增 * * *
如何理解实现Callable接口的方式创建多线程比实现Runnable接口创建多线程方式强大? * 1. call()可以有返回值的。 * 2.
call()可以抛出异常,被外面的操作捕获,获取异常的信息 * 3. Callable是支持泛型的 * * @author shkstart *
@create 2019-02-15 下午 6:01 */ //1.创建一个实现Callable的实现类 class NumThread implements
Callable{ //2.实现call方法,将此线程需要执行的操作声明在call()中 @Override public Object call()
throws Exception { int sum = 0; for (int i = 1; i <= 100; i++) { if(i % 2 == 0){
System.out.println(i); sum += i; } } return sum; } } public class ThreadNew {
public static void main(String[] args) { //3.创建Callable接口实现类的对象 NumThread
numThread= new NumThread();
//4.将此Callable接口实现类的对象作为传递到FutureTask构造器中,创建FutureTask的对象 FutureTask futureTask
= new FutureTask(numThread);
//5.将FutureTask的对象作为参数传递到Thread类的构造器中,创建Thread对象,并调用start() new Thread(
futureTask).start(); try { //6.获取Callable中call方法的返回值
//get()返回值即为FutureTask构造器参数Callable实现类重写的call()的返回值。 Object sum = futureTask.get
(); System.out.println("总和为:" + sum); } catch (InterruptedException e) { e.
printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
import java.util.concurrent.ExecutorService; import java.util.concurrent.
Executors; import java.util.concurrent.ThreadPoolExecutor; 4、创建线程的方式四:使用线程池 /**
* 创建线程的方式四:使用线程池 * * 好处: * 1.提高响应速度(减少了创建新线程的时间) *
2.降低资源消耗(重复利用线程池中线程,不需要每次都创建) * 3.便于线程管理 * corePoolSize:核心池的大小 *
maximumPoolSize:最大线程数 * keepAliveTime:线程没有任务时最多保持多长时间后会终止 * * *
面试题:创建多线程有几种方式?四种! * @author shkstart * @create 2019-02-15 下午 6:30 */ 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 service1 = (
ThreadPoolExecutor) service; //设置线程池的属性 //
System.out.println(service.getClass()); // service1.setCorePoolSize(15); //
service1.setKeepAliveTime(); //2.执行指定的线程的操作。需要提供实现Runnable接口或Callable接口实现类的对象
service.execute(new NumberThread());//适合适用于Runnable service.execute(new
NumberThread1());//适合适用于Runnable // service.submit(Callable
callable);//适合使用于Callable //3.关闭连接池 service.shutdown(); } }
<>二、、线程的常用方法
* 1. start():启动当前线程;调用当前线程的run() * 2. run():
通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中* 3. currentThread():静态方法,返回执行当前代码的线程 * 4.
getName():获取当前线程的名字 * 5. setName():设置当前线程的名字 * 6. yield():释放当前cpu的执行权 * 7. join(
):在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才 * 结束阻塞状态。 * 8. stop():
已过时。当执行此方法时,强制结束当前线程。* 9. sleep(long millitime):
让当前线程“睡眠”指定的millitime毫秒。在指定的millitime毫秒时间内,当前* 线程是阻塞状态。 * 10. isAlive():
判断当前线程是否存活* * * 线程的优先级: * 1. * MAX_PRIORITY:10 * MIN _PRIORITY:1 *
NORM_PRIORITY:5 -->默认优先级 * 2.如何获取和设置当前线程的优先级: * getPriority():获取线程的优先级 *
setPriority(int p):设置线程的优先级 * *
说明:高优先级的线程要抢占低优先级线程cpu的执行权。但是只是从概率上讲,高优先级的线程高概率的情况下*
被执行。并不意味着只有当高优先级的线程执行完以后,低优先级的线程才执行。
<>三、线程安全问题
一、 同步代码块解决 * 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式 * * 1.问题:卖票过程中,出现了重票、错票 -->
出现了线程的安全问题* 2.问题出现的原因:当某个线程操作车票的过程中,尚未操作完成时,其他线程参与进来,也操作车票。 * 3.
如何解决:当一个线程a在操作ticket的时候,其他线程不能参与进来。直到线程a操作完ticket时,其他*
线程才可以开始操作ticket。这种情况即使线程a出现了阻塞,也不能被改变。* * * 4.在Java中,我们通过同步机制,来解决线程的安全问题。 * *
方式一:同步代码块* * synchronized(同步监视器){ * //需要被同步的代码 * * } * 说明:1.
操作共享数据的代码,即为需要被同步的代码。-->不能包含代码多了,也不能包含代码少了。 * 2.
共享数据:多个线程共同操作的变量。比如:ticket就是共享数据。* 3.同步监视器,俗称:锁。任何一个类的对象,都可以充当锁。 *
要求:多个线程必须要共用同一把锁。* * 补充:在实现Runnable接口创建多线程的方式中,我们可以考虑使用this充当同步监视器。 * 方式二:同步方法。
* 如果操作共享数据的代码完整的声明在一个方法中,我们不妨将此方法声明同步的。 * * * 5.同步的方式,解决了线程的安全问题。---好处 *
操作同步代码时,只能有一个线程参与,其他线程等待。相当于是一个单线程的过程,效率低。---局限性 * 使用同步代码块解决继承Thread类的方式的线程安全问题
* * 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式 * * 说明:在继承Thread类创建多线程的方式中,慎用this
充当同步监视器,考虑使用当前类充当同步监视器。1、使用继承Thread的方式 重要: 使用Window.clss作为同步锁, 原因: 因为我们每一个this
都不是同一个对象, 因为我们创建了三个窗口, 所以如果要使用Window.clss来充当唯一的锁; public class Windows extends
Thread { private static int ticket = 100; // 多个Thread对象要共享ticket票 // private
static Object obj = new Object(); // 需要加static, 因为同步监视器要是唯一的 @Override public
void run() { while (true) { // synchronized (obj) { // 正确 // synchronized
(this) { // 错误的方式,使用继承来创建thread,因为this不是同一个对象,不能充当共享资源的同步锁 synchronized (Window.
class) { // 类对象,只会加载一次,所以也是唯一的 if (ticket > 0) { try { Thread.sleep(100); }
catch (InterruptedException e) { e.printStackTrace(); } System.out.println(
Thread.currentThread().getName() + ": 卖票, 票号为: " + ticket); ticket--; } else {
break; } } } } } public class WindowsTest2 { public static void main(String[]
args) { Windows t1 = new Windows(); Windows t2 = new Windows(); Windows t3 = new
Windows(); t1.setName("窗口1"); t2.setName("窗口2"); t3.setName("窗口3"); t1.start();
t2.start(); t3.start(); } } 2、使用实现Runnable方式 重点: 因为只创建了一个Window对象, 所有线程都可以共享该对象,
所以使用this,充当同步锁 public class Windows1 implements Runnable { private int ticket =
100; @Override public void run() { while (true) { // 任何对象都可以充当其 同步监视器,
因为始终创建了一个实现Rnnable接口的实例; synchronized (this) { if (ticket > 0) { try { Thread.
sleep(50); } catch (InterruptedException e) { e.printStackTrace(); } System.out.
println(Thread.currentThread().getName() + "卖票,票号为: " + ticket); ticket--; }
else { break; } } } } } public class WindowsTest1 { public static void main(
String[] args) { Windows1 w = new Windows1(); 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(); } } 二、同步方法来解决 重点:
* 使用同步方法解决实现Runnable接口的线程安全问题 * * 关于同步方法的总结: * 1. 同步方法仍然涉及到同步监视器,只是不需要我们显式的声明。 *
2. 非静态的同步方法,同步监视器是:this * 静态的同步方法,同步监视器是:当前类本身 重点: 在synchronized前面加上static,
如果是静态的同步方法, 所有的对象都共享; 相当于 Xxx.class的方式 1、通过继承Thread来实现线程同步: // private
synchronized void show () { // 此时同步锁不是唯一的了. private static synchronized void
show() { // 锁就是 当前类对象, Xxx.class if (ticket > 0) { try { Thread.sleep(100); }
catch (InterruptedException e) { e.printStackTrace(); } System.out.println(
Thread.currentThread().getName() + ": 卖票, 票号为: " + ticket); ticket--; } } 重点:
只需要加上synchronized, 因为只创建一个window对象, 所以不需要加static, 相当于this 2、通过实现Runnable接口来创建线程:
private synchronized void show() { // 同步监视器(同步锁) 就是 this if (ticket > 0) { try {
Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println(Thread.currentThread().getName() + "卖票,票号为: " + ticket);
ticket--; } } 三、 解决线程安全问题的方式三:Lock锁 --- JDK5.0新增 * * 1. 面试题:synchronized 与
Lock的异同?* 相同:二者都可以解决线程安全问题 * 不同:synchronized机制在执行完相应的同步代码以后,自动的释放同步监视器 *
Lock需要手动的启动同步(lock()),同时结束同步也需要手动的实现(unlock()) * * 2.优先使用顺序: * Lock ->
同步代码块(已经进入了方法体,分配了相应资源)-> 同步方法(在方法体之外) * * * 面试题:如何解决线程安全问题?有几种方式
<>四、线程通信问题
线程通信的例子:使用两个线程打印 1-100。线程1, 线程2 交替打印 * * 涉及到的三个方法: * wait():
一旦执行此方法,当前线程就进入阻塞状态,并释放同步监视器。* notify():
一旦执行此方法,就会唤醒被wait的一个线程。如果有多个线程被wait,就唤醒优先级高的那个。* notifyAll():
一旦执行此方法,就会唤醒所有被wait的线程。* * 说明: * 1.wait(),notify(),notifyAll()
三个方法必须使用在同步代码块或同步方法中。* 2.wait(),notify(),notifyAll()
三个方法的调用者必须是同步代码块或同步方法中的同步监视器。* 否则,会出现IllegalMonitorStateException异常 * 3.wait(),
notify(),notifyAll()三个方法是定义在java.lang.Object类中。 * * 面试题:sleep() 和 wait()的异同? *
1.相同点:一旦执行方法,都可以使得当前的线程进入阻塞状态。 * 2.不同点:1)两个方法声明的位置不同:Thread类中声明sleep() ,
Object类中声明wait() * 2)调用的要求不同:sleep()可以在任何需要的场景下调用。 wait()必须使用在同步代码块或同步方法中 * 3
)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep()不会释放锁,wait()会释放锁。 class Thread1
implements Runnable { private int number = 1; @Override public void run() {
while (true) { synchronized (this) { this.notify(); // 唤醒被wait()的线程 if (number
<= 100) { try { Thread.sleep(50); } catch (InterruptedException e) { e.
printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":
number" + number); number++; try { // 调用wait()方法的线程,进入阻塞状态 this.wait(); } catch
(InterruptedException e) { e.printStackTrace(); } } else { break; } } } } }

技术
今日推荐
下载桌面版
GitHub
百度网盘(提取码:draw)
Gitee
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:766591547
关注微信