继承Thread
/** * @Author GocChin * @Date 2021/5/11 11:56 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: */ class MyThread extends Thread{ @Override public void run() { System.out.println(currentThread().getName()+"运行了"); } } class Test{ public static void main(String[] args) { MyThread myThread = new MyThread(); System.out.println(Thread.currentThread().getName()+":运行了"); myThread.start(); } }
实现Runable接口创建多线程
/** * @Author GocChin * @Date 2021/5/11 12:37 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: 实现Runable接口的方式创建多线程 * 1.创建一个实现了Runable接口的类 * 2.实现类去实现Runable中的抽象方法,run(); * 3.创建实现类的对象 * 4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 * 5.通过Thread类的对象调用start() */ class MThread implements Runnable{ @Override public void run() { for (int i = 0; i<100;i++){ if (i%2!=0){ System.out.println(i); } } } } public class ThreadTest1 { public static void main(String[] args) { //3.创建实现类的对象 MThread mThread = new MThread(); //4.将此对象作为参数传递到Thread类的构造器中,创建Thread类的对象 Thread thread = new Thread(mThread); thread.start(); } }
Thread和Runable创建多线程对比
开发中:优先使用Runable
1.实现的方式没有类的单继承的局限性。
2.实现的方式跟适合处理多个线程有共享数据的情况。
联系:Thread类中也实现了Runable,两种方式都需要重写run()。
实现Callable接口
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.FutureTask; /** * @Author GocChin * @Date 2021/5/11 13:03 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: */ class MCallable implements Callable<Integer> { @Override public Integer call() throws Exception { int sum=0; for(int i=0;i<100;i++){ sum+=i; } return sum; } } public class CallableTest { public static void main(String[] args) { //执行Callable 方式,需要FutureTask 实现实现,用于接收运算结果 FutureTask<Integer> integerFutureTask = new FutureTask<Integer>(new MCallable()); new Thread(integerFutureTask).start(); //接受线程运算后的结果 Integer integer = null; try { integer = integerFutureTask.get(); System.out.println(integer); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } }
与Runable相比,Callable功能更强大
相比run()方法可以有返回值
方法可以抛出异常
支持泛型的返回值
需要借助FutureTask类,比如获取返回结果
线程池创建的好处
提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理:
import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; /** * @Author GocChin * @Date 2021/5/11 13:10 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: */ class Thread1 implements Runnable{ @Override public void run() { for (int i=1;i<30;i++){ System.out.println(Thread.currentThread().getName() + ":" + i); } } } public class ThreadPool { public static void main(String[] args) { //创建线程池 ExecutorService executorService= Executors.newFixedThreadPool(10); Thread1 threadPool = new Thread1(); for (int i=0;i<5;i++){ //为线程池分配任务 executorService.submit(threadPool); } //关闭线程池 executorService.shutdown(); } }
Thread中的常用方法
线程的优先级等级
涉及的方法
说明
多线程卖票
基于实现Runable的方式实现多线程买票
package demo2; /** * @Author GocChin * @Date 2021/5/11 13:37 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: 创建三个窗口买票,总票数为100张,使用Runable接口的方式 * 存在线程安全问题,待解决 */ class Thread2 implements Runnable{ private int ticket=100; @Override public void run() { while (true){ if (ticket>0) { System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket); ticket--; }else { break; } } } } public class Test1 { public static void main(String[] args) { Thread2 thread2 = new Thread2(); Thread t1 = new Thread(thread2); Thread t2 = new Thread(thread2); Thread t3 = new Thread(thread2); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
实现结果,存在重复的票
如果在买票方法中加入sleep函数
public void run() { while (true){ if (ticket>0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket); ticket--; }else { break; } } }
则运行结果可能会出现-1,表示也是不正常的
理想情况
极端情况
在java中,我们通过同步机制,来解决线程的安全问题。
synchronized(同步监视器){ //需要被同步的代码 }
说明
修改之后的代码:
package demo2; /** * @Author GocChin * @Date 2021/5/11 13:37 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: 创建三个窗口买票,总票数为100张,使用Runable接口的方式 * 存在线程安全问题,待解决 */ class Thread2 implements Runnable{ private int ticket=100; Object object = new Object(); @Override public void run() { while (true){ synchronized(object) { //括号中的内容可以直接使用当前对象this去充当 if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket); ticket--; } else { break; } } } } } public class Test1 { public static void main(String[] args) { Thread2 thread2 = new Thread2(); Thread t1 = new Thread(thread2); Thread t2 = new Thread(thread2); Thread t3 = new Thread(thread2); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
结果
继承Thread的方式,去使用同步代码块,需要将声明的锁对象设为statci,否则创建的对象的同步监视器不唯一,就无法实现。
package demo2; /** * @Author GocChin * @Date 2021/5/11 14:45 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: */ class WindowsTest2 extends Thread{ private static int ticket=100; private static Object obj = new Object(); @Override public void run() { while (true){ synchronized (obj){ //这里不能使用this去充当,可以直接写一个Test.class 类也是对象 if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(getName()+":买票,票号为:"+ticket); ticket--; }else { break; } } } } } public class Test2{ public static void main(String[] args) { WindowsTest2 w1 = new WindowsTest2(); WindowsTest2 w2 = new WindowsTest2(); WindowsTest2 w3 = new WindowsTest2(); w1.setName("窗口一"); w2.setName("窗口二"); w3.setName("窗口三"); w1.start(); w2.start(); w3.start(); } }
如果操作共享数据的代码完整的声明在一个方法中,可以将此方法声明为同步的。
通过实现Runable的方式实现同步方法。
package demo2; /** * @Author GocChin * @Date 2021/5/11 13:37 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: 创建三个窗口买票,总票数为100张,使用Runable接口的方式 * 存在线程安全问题,待解决 */ class Thread3 implements Runnable { private int ticket = 100; @Override public void run() { while (true) { show(); } } private synchronized void show(){ if (ticket > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + ":买票,票号为:" + ticket); ticket--; } } } public class Test3 { public static void main(String[] args) { Thread3 thread3 = new Thread3(); Thread t1 = new Thread(thread3); Thread t2 = new Thread(thread3); Thread t3 = new Thread(thread3); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
通过实现继承Thread的方式实现同步方法。使用的同步监视器是this,则不唯一,就会报错。所以将该方法定义为static。当前的同步换时期就变成Test4.class了
package demo2; /** * @Author GocChin * @Date 2021/5/11 14:45 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: */ class WindowsTest4 extends Thread{ private static int ticket=100; private static Object obj = new Object(); @Override public void run() { while (true){ show(); } } public static synchronized void show(){//同步监视器不是this了,而是当前的类 // public synchronized void show(){//同步监视器是this ,t1,t2,t3 if (ticket>0){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+":买票,票号为:"+ticket); ticket--; } } } public class Test4{ public static void main(String[] args) { WindowsTest4 w1 = new WindowsTest4(); WindowsTest4 w2 = new WindowsTest4(); WindowsTest4 w3 = new WindowsTest4(); w1.setName("窗口一"); w2.setName("窗口二"); w3.setName("窗口三"); w1.start(); w2.start(); w3.start(); } }
总结
synchronize与lock的异同
相同
不同
建议优先使用顺序
Lock------>同步代码块(已经进入了方法体,分配了相应资源)---->同步方法(在方法体之外)
package demo2; import java.util.concurrent.locks.ReentrantLock; /** * @Author GocChin * @Date 2021/5/11 15:58 * @Blog: itdfq.com * @QQ: 909256107 * @Descript: */ class Lock1 implements Runnable{ private int ticket=50; //1.实例化 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.调用解锁方法 lock.unlock(); } } } } public class LockTest1 { public static void main(String[] args) { Lock1 lock1 = new Lock1(); Thread t1 = new Thread(lock1); Thread t2 = new Thread(lock1); Thread t3 = new Thread(lock1); t1.setName("窗口一"); t2.setName("窗口二"); t3.setName("窗口三"); t1.start(); t2.start(); t3.start(); } }
到此这篇关于java多线程创建及线程安全的文章就介绍到这了,更多相关java多线程创建内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
免责声明:本站发布的内容(图片、视频和文字)以原创、来自互联网转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系QQ:712375056 进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。
Copyright © 2009-2021 56dr.com. All Rights Reserved. 特网科技 特网云 版权所有 珠海市特网科技有限公司 粤ICP备16109289号
域名注册服务机构:阿里云计算有限公司(万网) 域名服务机构:烟台帝思普网络科技有限公司(DNSPod) CDN服务:阿里云计算有限公司 中国互联网举报中心 增值电信业务经营许可证B2
建议您使用Chrome、Firefox、Edge、IE10及以上版本和360等主流浏览器浏览本网站