线程路程的关系
“线程和进程的关系:每个进程都有相应的线程,在执行程序时,实际上是执行相应的一系列线程。进程是资源分配的最小单位,线程是程序执行的最小单位。”
概述
新建(NEW):新创建了一个线程对象。
可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。
运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。
阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:
死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生
实现方式
在java中可有两种方式实现多线程,一种是继承Thread类,一种是实现Runnable接口 。
线程启动的方式是调用start方法,start方法会调用run方法。
Runnable
Runnable是一个接口,里面就只有run方法。
public interface Runnable {
public abstract void run();
}
使用案例
public class MyRunnable implements Runnable{
@Override
public void run() {
try {
System.out.println("重写了runable");
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Runnable myRunnable0 = new MyRunnable();
Thread thread0 = new Thread(myRunnable0);
Thread thread1 = new Thread(myRunnable0);
//Runnable myRunnable1 = new MyRunnable();
//Thread thread1 = new Thread(myRunnable1);
thread0.start();
//thread1.start();
}
}
Thread
概念
hread类是在java.lang包中定义的。一个类只要继承了Thread类同时覆写了本类中的
run()方法就可以实现多线程操作了,但是一个类只能继承一个父类。
案例
/**
* @author xing
* 2021年9月27日下午12:11:11
* 继承Thread的用法
*/
public class MyThread extends Thread {
String str;
public void run() {
this.show();
}
public void show() {
while(true) {
try {
Thread.sleep(1000);//当前线程睡一秒
System.out.println(str+" :-----------MyThread-----------");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
MyThread mt0 = new MyThread();
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
mt0.str = "mt0";
mt0.start();
mt1.str = "mt1";
mt1.start();
mt2.str = "mt2";
mt2.start();
}
}
注:每new一个thread就是开辟了一个新的线程,start就是启动了线程。main所运行的就是主线程,主线程可以开启子线程,各个线程干自己的事情。
Callable
当我们执行线程需要返回值的时候那么就必须选用实现Callable类的方式,因为目前只有这种方式能返回值。当然这种方式我们也可以不需要获取返回值。
这种方式是通过FutureTask的get()方法(下面代码的第22行)或者get(long timeout, TimeUnit unit)(下面代码的第28行)方法获取返回值。当我们看Callable的接口定义的源码会发现“public interface Callable<V> ” ,我们实现的时候是需要定义返回类型,如下面代码所示。
除此之外我们还需要注意的是:当我们通过FutureTask的get()方法去获取线程的返回值的时候是要等到线程call()内容都执行完毕之后才能获取得到,并且get()方法后面的代码必须等待,说明这一定是同步的,所以我们可以在真正需要线程返回值的时候才通过get()方法去获取,以免被阻塞。当我们通过get(long timeout, TimeUnit unit)方式去获取的时候可以设置超时时间,如果超过所设置的超时时间都没有获取到线程返回的值则会抛出 java.util.concurrent.TimeoutException 异常,当然如果在get(long timeout, TimeUnit unit)之前用get()方式获取了的话就不会抛异常。
实现Callable还有个好处就是可以线程可以抛异常,如果我们需要在线程里抛出异常的话也可以选用这种方式,其他两种方式只能捕获异常信息。
public class ThreadCallableDemo implements Callable<Integer>{
/** 计数变量 */
private int count = 0;
public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
ThreadCallableDemo threadCallableDemo = new ThreadCallableDemo();
//通过FutureTask获取返回值
FutureTask<Integer> taskA = new FutureTask<>(threadCallableDemo);
//实例化线程
Thread thread = new Thread(taskA, "threadCallableDemoA");
System.out.println(String.format("线程状态preStart: %s", thread.getState()));
//启动线程
thread.start();
System.out.println(String.format("线程状态afterStart: %s", thread.getState()));
//通过FutureTask的get()方法获取返回值
int result = taskA.get();
System.out.println("是否同步测试....");
System.out.println(String.format("result: %s", result));
System.out.println(String.format("线程状态afterGetResult1: %s", thread.getState()));
//通过FutureTask的get()方法获取返回值 设置超时时间 单位为ms
int resultWithTime = taskA.get(100, TimeUnit.MILLISECONDS);
System.out.println(String.format("resultWithTime: %s", resultWithTime));
System.out.println(String.format("线程状态afterGetResult2: %s", thread.getState()));
}
/**
* 实现Callable的call类
*/
@Override
public Integer call() throws Exception {
//自增
count++;
System.out.println(String.format("线程名称:%s, 线程状态:%s, count:%s",
Thread.currentThread().getName(), Thread.currentThread().getState(), count));
System.out.println("休眠1000ms....");
Thread.currentThread().sleep(1000);
return count;
}
}
synchronized
synchronized是Java中的关键字,是一种同步锁。它修饰的对象有以下几种:
-
修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调用这个代码块的对象;
-
修饰一个方法,被修饰的方法称为同步方法,其作用的范围是整个方法,作用的对象是调用这个方法的对象;
-
修改一个静态的方法,其作用的范围是整个静态方法,作用的对象是这个类的所有对象;
-
修改一个类,其作用的范围是synchronized后面括号括起来的部分,作用主的对象是这个类的所有对象。
synchronized修饰方法案例:
窗口售票实例:
源码
package Dome1;
public class TicketDome extends Thread{
private static int sum=100;
private static Object obj=new Object();
@Override
public void run() {
while (true){//同步代码块
sum.sleep(1000);
synchronized (obj){
if (sum>0) {
System.out.println(Thread.currentThread().getName()+"售出的票号是"+sum);
sum--;
}else {
System.exit(0);
}
}
}
}
public synchronized void mp(){
}
public static void main(String[] args) {
TicketDome ticketDome1 = new TicketDome();
ticketDome1.start();
TicketDome ticketDome2 = new TicketDome();
ticketDome2.start();
TicketDome ticketDome3 = new TicketDome();
ticketDome3.start();
}
}
不加入重度锁
源码
package Dome1;
public class TicketDome extends Thread{
private static int sum=100;
private static Object obj=new Object();
@Override
public void run() {
while (true){//同步代码块
sum.sleep(1000);
if (sum>0) {
System.out.println(Thread.currentThread().getName()+"售出的票号是"+sum);
sum--;
}else {
System.exit(0);
}
}
}
public synchronized void mp(){
}
public static void main(String[] args) {
TicketDome ticketDome1 = new TicketDome();
ticketDome1.start();
TicketDome ticketDome2 = new TicketDome();
ticketDome2.start();
TicketDome ticketDome3 = new TicketDome();
ticketDome3.start();
}
}
131

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



