Java入门笔记 05-多线程
2024-09-06 22:26:57
介绍:Java提供了非常优秀的多线程支持,程序可以通过非常简单的方式来启动多线程。本章主要内容为:多线程的创建、启动、控制以及同步操作,并介绍JDK 5新增的线程创建方式。
一、线程的创建与使用:
1. 继承Thread类创建线程:
- 建立一个继承Thread的子类
- 重写Thread类的run()--把所执行的操作写在该方法中
- 创建一个子类对象
- 通过该对象调用start()方法
//1.1. 建立一个继承Thread的子类
class MyThread1 extends Thread{
@Override
//1.2. 重写Thread类的run()
public void run() {
for(int i=;i<;i++){
if(i%==){
System.out.println(getName() + ":" + i);//Thread.currentThread().getName()
}
}
}
} //1.3. 创建一个子类对象
MyThread1 t1 = new MyThread1();
t1.setName("线程1"); //1.4. 通过该对象调用start()方法
t1.start();//注意,一个线程对象不能同时两次start,需要重新创建一个对象来start
2. 实现Runnable接口创建线程:
- 建立一个Runnable接口的实现类
- 实现Runnable中的抽象方法run()
- 创建实现类对象
- 将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
- 通过Thread类的对象调用start()方法
//2.1 建立一个Runnable接口的实现类
class MyThread2 implements Runnable{
//2.2. 实现Runnable中的抽象方法run()
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} //2.3 2.4 创建实现类对象,将此对象作为参数传递到Thread类的构造器中,创建一个Thread类的对象
Thread t2 = new Thread(new MyThread2());
t2.setName("线程2"); //2.5. 通过Thread类的对象调用start()方法
t2.start();
开发中优先选择后者:既没有类的单继承性的限制,而且实现的方式更适合来处理多个线程有共享数据的情况。
3. 实现Callable接口创建线程:
- 创建一个Callable接口的实现类
- 实现call方法,将此线程需要执行的操作写在方法体中
- 创建Callable接口实现类的对象
- 将上面的实现类对象作为参数创建FutureTask的对象
- 将上面的FutureTask的对象作为参数创建Thread的对象
- 利用Thread的对象调用start方法
- 通过 FutureTask的对象.get() 方法获取call方法的返回值
//3.1 创建一个Callable接口的实现类
class NumThread implements Callable{
@Override
//3.2 实现call方法,将此线程需要执行的操作写在方法体中
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;
}
} //3.3 创建Callable接口实现类的对象
NumThread num = new NumThread();
//3.4 将上面的实现类对象作为参数创建FutureTask的对象
FutureTask futureTask = new FutureTask(num);
//3.5 将上面的FutureTask的对象作为参数创建Thread的对象
Thread t = new Thread(futureTask);
t.setName("线程3");
//3.6 利用Thread的对象调用start方法
t.start(); try {
//3.7 通过 FutureTask的对象.get() 方法获取call方法的返回值
Object sum = futureTask.get();//get方法的返回值即为NumThread.call的返回值
System.out.println("共计:" + sum);
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
相较于Runnable接口更强大、可以抛出异常、支持泛型,但是需要借助FutureTask类
4. 使用线程池创建线程:
- 创建指定线程数量的线程池
- 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
- 关闭连接池
class MyThread2 implements Runnable{
@Override
public void run() {
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(Thread.currentThread().getName() + ":" + i);
}
}
}
} //4.1 创建指定线程数量的线程池
ExecutorService service = Executors.newFixedThreadPool(10);//创建确定个数的线程池
// 4.2 执行指定的线程的操作,需要提供一个实现Runnable获Callable接口的对象
service.execute(new MyThread2());//参数只能是Runnable
service.execute(new MyThread3());//参数只能是Runnable
//4.3 关闭连接池
service.shutdown();
5. Thread类中常见的方法:
- void start(): 启动线程,并执行对象的run()方法
- run(): 线程在被调度时执行的操作
- String getName(): 返回线程的名称
- void setName(String name):设置该线程名称
- static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
- static void yield(): 线程让步,暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法
- join() : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,使得低优先级的线程也可以获得执行
- static void sleep(long millis):(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。(抛出InterruptedException异常)
- stop(): 强制线程生命期结束,不推荐使用
- boolean isAlive(): 返回boolean,判断线程是否还活着
//给线程命名--方式一:通过setName()
t1.setName("线程1"); //设置该线程名称 //给线程命名--方式二:通过构造器
ThreadMethodTest2 t2 = new ThreadMethodTest2("*线程2");
6. 线程优先级:
- MAX_PRIORITY:10
- MIN _PRIORITY:1
- NORM_PRIORITY:5
涉及的方法:
- getPriority() :返回线程优先值
- setPriority(int newPriority) :改变线程的优先级
说明:
- 线程创建时继承父线程的优先级
- 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用
二、线程的同步:
1. 同步代码块:
synchronized (同步监视器){
// 需要被同步的代码;
}
同步监视器:俗称锁。任何一个类的对象都可以充当锁,要求多个线程必须共用同一个锁。
- Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
- 继承Thread类的方式不能用this,可以使用 类.class 来充当
2. 同步方法:synchronized放在方法声明中,表示整个方法为同步方法。同步方法仍然涉及同步监视器,但无需显示说明,例如:
public synchronized void show (String name){
....
}
3. lock锁:
//1. 实例化ReentrantLock
try{
//2. 调用lock
//需要被同步的代码
}finally{
//3. 调用unlock
}
4. 案例:
/**
* 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
* 存在线程的安全问题,采用 同步代码块方式 解决
* synchronized(同步监视器){
* //需要被同步的代码
* }
* > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
* 要求多个线程必须共用同一个锁。
* > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
* > 继承Thread类的方式不能用this,可以使用 类.class 来充当
* @author shkstart
* @create 2019-02-13 下午 4:47
*/
class Window1 implements Runnable{ private int ticket = 100; //共享数据
private Object obj = new Object(); @Override
public void run() {
while(true){
synchronized (obj){ //synchronized(this)
if(ticket > 0){
System.out.println(Thread.currentThread().getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
} public class TicketSafeBR {
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();
} }
Runnable-同步代码块方式
/**
* 例子:创建三个窗口卖票,总票数为100张.使用实现Runnable接口的方式
* 存在线程的安全问题,采用 同步方法方式 解决
*
* > 同步方法仍然涉及同步监视器,但无需显示说明
* > 非静态同步方法的同步监视器是:this
* > 静态同步方法的同步监视器是:类.class
*
*
* @author shkstart
* @create 2019-02-13 下午 4:47
*/
class Window2 implements Runnable{ private int ticket = 100; @Override
public void run() {
while(true){
show();
}
} 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--;
}
}
} public class TicketSafeMR {
public static void main(String[] args) {
Window2 w = new Window2(); 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-同步方法方式
/**
*
* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
*
* 存在线程的安全问题,采用 同步代码块方式 解决
* synchronized(同步监视器){
* //需要被同步的代码
* }
* > 同步监视器:俗称锁。任何一个类的对象都可以充当锁,
* 要求多个线程必须共用同一个锁。
* > Runnable接口的方式的所可以使用this,因为它自始至终只用了一个对象
* > 继承Thread类的方式不能用this,可以使用 类.class 来充当(类.class只会加载一次)(反射)
* @author shkstart
* @create 2019-02-13 下午 4:20
*/
class Window extends Thread{
private static int ticket = 100;
//要求多个线程必须共用同一个锁,因此使用了static,
private static Object obj = new Object();//一定要注意,这里与Runnable接口的方式有所不同
@Override
public void run() { while(true){
synchronized (obj){ //synchronized(Window.class)
if(ticket > 0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + ":卖票,票号为:" + ticket);
ticket--;
}else{
break;
}
}
}
}
} public class TicketSafeBT {
public static void main(String[] args) {
Window t1 = new Window();
Window t2 = new Window();
Window t3 = new Window(); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); }
}
Thread-同步代码块方式
/**
*
* 例子:创建三个窗口卖票,总票数为100张.使用继承Thread类的方式
*
* 存在线程的安全问题,使用 同步方法方式 来解决
*
* > 同步方法仍然涉及同步监视器,但无需显示说明
* > 非静态同步方法的同步监视器是:this
* > 静态同步方法的同步监视器是:类.class
*
*
* @author shkstart
* @create 2019-02-13 下午 4:20
*/
class Window3 extends Thread{ private static int ticket = 100;
@Override
public void run() {
while(true){
show();
}
} private static synchronized void show(){ //同步监视器:Window3.class
//private static synchronized void show(){ //此种方法错误,同步监视器: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 TicketSafeMT {
public static void main(String[] args) {
Window3 t1 = new Window3();
Window3 t2 = new Window3();
Window3 t3 = new Window3(); t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3"); t1.start();
t2.start();
t3.start(); }
}
Thread-同步方法方式
public class LockTest {
public static void main(String[] args) {
Window4 w = new Window4(); 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(); } } class Window4 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
lock.unlock();
}
}
}
}
Lock锁方式
三、线程的通信:
- wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有权后才能继续执行。
- notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待
- notifyAll ():唤醒正在排队等待资源的所有线程结束等待.
/**
* 线程的通信:
* 两个线程交替打印1-100:
* > wait()
* > notify()/notifyAll()
* > 注意:wait和notify的调用者必须是同一个对象
* > wait和notify必须使用在同步代码块或者同步方法中
*
* @author marc
* @creat 2020-02-07 上午9:22
*/
public class MsgTest {
public static void main(String[] args) {
Number n1 = new Number(); Thread t1 = new Thread(n1);
Thread t2 = new Thread(n1); t1.setName("线程1");
t2.setName("线程2"); t1.start();
t2.start(); }
} class Number implements Runnable{
private int i = 1;
@Override
public void run() {
while (true){ synchronized (this) { notify(); //this.notify() if(i<=100){
System.out.println(Thread.currentThread().getName() + ":" + i);
i++; try {
wait(); //this.wait()
} catch (InterruptedException e) {
e.printStackTrace();
} }else
break;
} }
}
}
最新文章
- 使用静态函数impl模式做接口
- clear属性
- mvc中测试网络
- hadoop小试
- 关于Mesos和Kubernetes的区别
- Android-----工程文件目录介绍
- Ubuntu下的杀毒
- .net中SessionState相关配置
- Asset Store
- 理解运算符 || 和 &;&; 及方法
- Shiro--权限控制
- linux设备驱动归纳总结(五):1.在内核空间分配内存【转】
- javascript 中 typeof 的使用
- Codeforces Gym 100231B Intervals 线段树+二分+贪心
- c++程序开发利器
- EXT经验--查询EditorGridPanel的tbar的默认配置对象
- hdu 3927 Math Geek
- INSTALLING QUARTUS II V.13.1 64 BIT ON RHEL/CENTOS 6 64 BIT
- [原]命令模式在MVC框架中的应用
- this 指针