文章目录
Java多线程
三种创建方式:Thread类、Runnable接口、Callable接口
1、继承Thread类
(1)自定义线程类
(2)重写run()方法,编写线程执行体
(3)创建线程对象,调用start()方法执行启动线程
run()和start()区别:
public class TestThread01 extends Thread {
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
TestThread01 testThread01 = new TestThread01();
testThread01.run();
for (int i = 0; i <20; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
run方法:先执行run线程再执行mian线程,有先后顺序。
public class TestThread02 implements Runnable {
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
TestThread01 testThread01 = new TestThread01();
Thread thread = new Thread(testThread01);
thread.start();
for (int i = 0; i <2000 ; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
start方法:该线程和main线程的执行是交替的
注意:线程开启不一定立即执行,由CPU调度执行。
练习:使用多线程同步下载图片
2、实现Runnable接口
推荐使用,因为Java单继承的局限性
1、定义MyRunnable类实现Runnable接口
2、实现run()方法,编写线程执行体
3、创建线程对象,调用start()方法启动线程
public class TestThread02 implements Runnable {
@Override
public void run() {
for (int i = 0; i <20 ; i++) {
System.out.println("我在看代码---"+i);
}
}
public static void main(String[] args) {
//创建runnable接口实现类对象
TestThread02 testThread02 = new TestThread02();
//创建线程对象,通过线程对象来开启我们的线程,代理
new Thread(testThread02).start();
for (int i = 0; i <2000 ; i++) {
System.out.println("我在学习多线程--"+i);
}
}
}
3、多线程操作同一对象
买火车票的例子:
public class BuyTrianTicket implements Runnable {
private int ticktNums = 10;
@Override
public void run() {
while(true){
if(ticktNums<=0){
break;
}
//模拟延时
try {
Thread.sleep(200);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticktNums--+"票");
}
}
public static void main(String[] args) {
BuyTrianTicket ticket = new BuyTrianTicket();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛党").start();
}
}
(线程不安全导致票数为负)

龟兔赛跑例子:
- 模拟龟兔赛跑:
- 1.赛道 距离 Race
- 2.比赛是否结束
- 3.打印胜利者
- 4.龟兔赛跑开始
- 5.结果是兔子睡觉,乌龟赢了。
- 6.胜利者是乌龟
public class Race implements Runnable{
//胜利者
private static String winner;
@Override
public void run() {
for (int i = 0; i <= 100 ; i++) {
//模拟兔子休息
if (Thread.currentThread().getName().equals("兔子") && i%20==0) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//判断比赛是否结束
boolean flag = gameOver(i);
if (flag){
break;
}
System.out.println(Thread.currentThread().getName()+"跑了"+i+"米");
}
}
//判断是否完成比赛
private boolean gameOver(int steps){
//判断是否有胜利者
if (winner!=null)
return true;
else {
if (steps>=100){
winner = Thread.currentThread().getName();
System.out.println("winner is "+winner);
return true;
}
}
return false;
}
public static void main(String[] args) {
Race race =new Race();
new Thread(race,"兔子").start();
new Thread(race,"乌龟").start();
}
}

4、实现Callable接口
1.实现Callable接口,需要返回值
2.重写call方法,需要抛出异常
3.创建目标对象
4.创建执行服务:ExecutorService ser = Executors.newFixThreadPool();
5.提交执行:Future result1 = ser.submit(t1);
6.获取结果:boolean b1 = result1.get();
7.关闭服务:ser.shutdownNow();
网图下载案例:
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class TestCallable implements Callable<Boolean> {
private String url; //网络图片地址
private String name;//保存的文件名
public TestCallable(String url,String name) {
this.url = url;
this.name = name;
}
//下载图片线程的执行体
@Override
public Boolean call() throws Exception {
WebDownloader webDownloader = new WebDownloader();
webDownloader.downloader(url,name);
System.out.println("下载了文件名为:"+name);
return true;
}
public static void main(String args[]) throws InterruptedException, ExecutionException {
TestCallable t1 = new TestCallable("https://www.baidu.com/img/flexible/logo/pc/result.png","1.png");
TestCallable t2 = new TestCallable("https://img-home.csdnimg.cn/images/20201124032511.png","2.png");
TestCallable t3 = new TestCallable("https://img-home.csdnimg.cn/images/20201124032511.png","3.png");
//创建执行服务:
ExecutorService ser = Executors.newFixedThreadPool(3);
//提交执行:
Future<Boolean> r1 = ser.submit(t1);
Future<Boolean> r2 = ser.submit(t2);
Future<Boolean> r3 = ser.submit(t3);
//获取结果:
boolean b1 = r1.get();
boolean b2 = r2.get();
boolean b3 = r3.get();
//关闭服务:
ser.shutdownNow();
}
}
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import org.apache.commons.io.FileUtils;
class WebDownloader{
//下载方法
public void downloader(String url,String name) {
try {
FileUtils.copyURLToFile(new URL(url),new File(name));
} catch (MalformedURLException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}

5、sleep线程休眠
-
sleep(时间)指定当前线程阻塞的毫秒数
-
sleep存在异常InterruptedException;
-
sleep时间到达后进入就绪态;
-
sleep可以模拟网络延时,倒计时等;
-
每一个对象都有一个锁,sleep不会释放锁;
打印系统时间:
import java.text.SimpleDateFormat;
import java.util.Date;
public class TestSleep2 {
public static void main(String[] args) {
//打印当前系统时间
Date startTime = new Date(System.currentTimeMillis());
while(true){
try {
Thread.sleep(1000);
System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
startTime = new Date(System.currentTimeMillis());//更新当前时间
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void tenDown() throws InterruptedException {
int num = 10;
while(true){
Thread.sleep(1000);
System.out.println(num--);
if(num<=0)
break;
}
}
}

6、Yield 线程礼让
- 礼让线程,让当前正在执行的线程暂停,但不阻塞。
- 将线程从运行态转换为就绪态。
- 让CPU重新调度,礼让不一定成功。
public class TestYield {
public static void main(String[] args) {
MyYield myYield = new MyYield();
new Thread(myYield,"a").start();
new Thread(myYield,"b").start();
}
}
class MyYield implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程开始执行");
Thread.yield();//礼让
System.out.println(Thread.currentThread().getName()+"线程停止执行");
}
}
礼让成功:

礼让失败:

6、Join 线程强制执行
Join合并线程,待此线程执行完成后,再执行其他线程,其他线程阻塞
//测试 JOIN方法
public class TestJoin implements Runnable{
@Override
public void run() {
for (int i = 0; i <5 ; i++) {
System.out.println("线程VIP来了"+i);
}
}
public static void main(String[] args) throws InterruptedException {
//启动我们的线程
TestJoin testJoin = new TestJoin();
Thread thread = new Thread(testJoin);
thread.start();
//主线程
for (int i = 0; i <20 ; i++) {
if ( i == 3){
thread.join();//插队
}
System.out.println("main"+i);
}
}
}

7、线程的状态
public class TestState {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(()->{
for (int i = 0; i <5 ; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("//////");
});
//观察状态
Thread.State state = thread.getState();
System.out.println(state);//new
//观察启动后
thread.start();
state = thread.getState();
System.out.println(state);//Run
while(state!= Thread.State.TERMINATED){
//只要线程不终止,就一直输出状态
Thread.sleep(500);
state = thread.getState();//更新输出状态
System.out.println(state);//输出状态
}
}
}

7、线程优先级
8、守护线程Daemon
- 线程分为用户线程和守护线程
- 虚拟机逆序确保用户线程执行完毕
- 虚拟机不用等待守护线程执行完毕
//测试守护线程
//上帝守护你
public class TestDaemon {
public static void mian(String args[]) {
God god =new God();
You you= new You();
Thread thread = new Thread(god);
thread.setDaemon(true);//默认是false表示用户线程,正常线程都是用户线程
thread.start();//上帝守护线程启动
new Thread(you).start();// 你线程启动。。
}
}
//上帝
class God implements Runnable{
@Override
public void run() {
System.out.println("god blesses you");
}
}
//你
class You implements Runnable{
@Override
public void run() {
for(int i=0;i<36500;i++){
System.out.println("happy everyday");
}
System.out.println("=======goodbye world=======");
}
}

9、线程同步锁机制 synchronized
synchronized关键字,包括两种用法:
synchronized 方法 和 synchronized 块:
同步方法:public synchronized void method(int args){}
synchronized方法控制对”对象“的访问,每一个对象对应一把锁,每一个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,方法一旦执行,就独占该锁,直到该方法返回才释放锁,后面被阻塞的线程才能获得这个锁,继续执行。
缺陷:若将一个大的方法声明为synchronized 将会影响效率。
方法一:
public class RunnableImpl implements Runnable {
private int ticket =100;
@Override
public void run(){
System.out.println("this:"+this);
while(true){
payTicket();
}
}
public synchronized void payTicket(){
if(ticket>0){
try{
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
方法二:
public class RunnableImpl implements Runnable {
private int ticket = 100;
Object obj = new Object();
@Override
public void run(){
System.out.println("this:"+this);
while(true){
payTicket();
synchronized (obj){
if(ticket>0){
try{
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
}
public void payTicket(){
if(ticket>0){
try{
Thread.sleep(10);
}catch (InterruptedException e){
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"-->正在卖第"+ticket+"张票");
ticket--;
}
}
}
public class Demo01Ticket {
public static void main(String[] args) {
RunnableImpl run = new RunnableImpl();
System.out.println("run:"+run);
Thread t0 = new Thread(run);
Thread t1 = new Thread(run);
Thread t2 = new Thread(run);
t0.start();
t1.start();
t2.start();
}
}
同步块:synchronized (obj) { }
obj 称之为 同步监视器
1.obj可以是任何对象,但是推荐使用共享资源作为同步监视器
2.同步方法中无需指定同步监视器,因为同步方法的同步监视器就算是this,就是这个对象本身,或者class
list举例:线程不安全,size小于10000,是因为多个线程同时访问一个位置,数据被覆盖了。
import java.util.ArrayList;
public class UnsafeList {
public static void main(String []args) {
ArrayList<String> list = new ArrayList<String>();
for (int i =0 ;i<10000; i++) {
new Thread(()->{
list.add(Thread.currentThread().getName());
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}

public class UnsafeList {
public static void main(String []args) {
ArrayList<String> list = new ArrayList<String>();
for (int i =0 ;i<10000; i++) {
new Thread(()->{
synchronized(list){
list.add(Thread.currentThread().getName());
}
}).start();
}
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(list.size());
}
}

10、Lock(锁)
-
java.util.concurrent.locks.Lock接口是控制多个线程对共享资源进行访问的工具。锁提供了对共享资源的独占访问。
-
ReentrantLock类实现了Lock,它拥有与synchronized相同的并发性和内存语义,在实现线程安全的控制中,比较常用ReentrantLock,可以显示加锁释放锁。
import java.util.concurrent.locks.ReentrantLock;
public class TestLock implements Runnable {
private int ticktNums = 10;
//定义锁
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
lock.lock();
if(ticktNums>0){
try {
// Thread.sleep(200);
}catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"拿到了第"+ticktNums--+"票");
}else {
break;
}
lock.unlock();
}
}
public static void main(String[] args) {
TestLock ticket = new TestLock();
new Thread(ticket,"小明").start();
new Thread(ticket,"老师").start();
new Thread(ticket,"黄牛党").start();
}
}
加锁后正常拿票。


10万+

被折叠的 条评论
为什么被折叠?



