11.多线程与锁
多线程
写法1:类
- 继承自
Thread
类
package org.example;
class Worker extends Thread{
@Override
public void run()
{
for(int i = 0; i < 10; i ++)
{
System.out.println("Hello: " + this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Main {
public static void main(String[] args) {
Worker w1 = new Worker();
Worker w2 = new Worker();
w1.setName("Thread-1");
w2.setName("Thread-2");
w1.start();
w2.start();
//若要等待w1线程结束再执行下面的代码
w1.join();
w2.join();
System.out.println("Main is over~");
}
}
写法2:接口
- 实现
Runnable
接口
这种写法可以使用同一个
Worker
的实例分别实现两个线程,天然地便于同步的实现。
package org.example;
import java.sql.SQLOutput;
class Worker implements Runnable{
@Override
public void run()
{
for(int i = 0; i < 10; i ++)
{
System.out.println("Hello : " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker w = new Worker();
new Thread(w).start();
new Thread(w).start();
Thread.sleep(1000);
System.out.println("Main is over~");
}
}
常见的API
start()
:开启一个线程Thread.sleep()
:休眠一个线程join()
:等待线程结束interrupt()
:将休眠中的线程结束setDaemon()
:将线程设置为守护线程,
守护线程:当该线程成为最后一个运行的线程时,操作系统会强制结束它。
如:
package org.example;
class Worker extends Thread{
@Override
public void run()
{
for(int i = 0; i < 10; i ++)
{
System.out.println("Hello: " + i + " " + this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//休眠中检测到interrupt调用
System.out.println("Be interrupted!");
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker w1 = new Worker();
Worker w2 = new Worker();
w1.setName("Thread-1");
w2.setName("Thread-2");
w1.start();
w2.start();
w1.join(5000); //参数5000:最多等待5000ms
w1.interrupt();
System.out.println("Main is over~");
}
}
又如:
package org.example;
class Worker extends Thread{
@Override
public void run()
{
for(int i = 0; i < 10; i ++)
{
System.out.println("Hello: " + i + " " + this.getName());
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//休眠中检测到interrupt调用
System.out.println("Be interrupted!");
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker w1 = new Worker();
Worker w2 = new Worker();
w1.setName("Thread-1");
w2.setName("Thread-2");
//设置为守护线程
w1.setDaemon(true);
w2.setDaemon(true);
w1.start();
w2.start();
Thread.sleep(5000);
System.out.println("Main is over~");
}
}
w1
、w2
被设置为守护线程,当主线程执行结束后w1
、w2
虽未结束却也被操作系统强行结束。
锁
不同的线程竞争同一个资源可能造成读写冲突,为了解决这个问题,引入锁。
package org.example;
import java.util.concurrent.locks.ReentrantLock;
class Worker extends Thread{
public static int cnt = 0; //互斥资源
private static final ReentrantLock lock = new ReentrantLock(); //锁
@Override
public void run()
{
for(int i = 0; i < 200000; i ++)
{
lock.lock(); //获取cnt前先获取锁
try
{
cnt ++;
}
finally {
//无论如何cnt++后释放锁
lock.unlock();
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker w1 = new Worker();
Worker w2 = new Worker();
w1.setName("Thread-1");
w2.setName("Thread-2");
w1.start();
w2.start();
w1.join();
w2.join();
System.out.println(Worker.cnt);
}
}
写法1:类
将Synchronized
加到代码块上:
package org.example;
class Count
{
public int cnt = 0;
}
class Worker extends Thread{
public final static Count count = new Count();
@Override
public void run()
{
//给count加锁,注意synchronized只能作用于对象
synchronized(count)
{
for(int i = 0; i < 200000; i ++)
count.cnt ++;
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Worker();
Thread t2 = new Worker();
t1.start();
t2.start();
t1.join();
t2.join();
//输出最后的cnt
System.out.println(Worker.count.cnt);
}
}
写法2:接口
使用接口创建类,使用类对象创建Thread
类,这样Thread
的变量本身就互斥。
package org.example;
class Worker implements Runnable{
public static int cnt = 0;
@Override
public synchronized void run()
{
for(int i = 0; i < 200000; i ++)
cnt ++;
}
//上面对函数加同步,等价于对该类对象的this加锁
//即:synchronized(this){ 临界区代码 }
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Worker w = new Worker();
//t1, t2采用同一个Worker对象构造Thread对象
Thread t1 = new Thread(w);
Thread t2 = new Thread(w);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(Worker.cnt);
}
}
wait与notify
wait()
:将当前线程挂起,可加参数,代表最多挂起多少时间(单位是毫秒);
调用方式示例:
synchronized(a)
{
a.wait(4000); //挂起4000ms等待被唤醒
}
notify()
:随机唤醒一个线程;notifyAll()
:唤醒所有线程;
以下实现一个例子:初始化5个线程,然后再初始化另一个线程,用它去唤醒前面的5个线程。
package org.example;
class Worker extends Thread
{
public final Object object;
public final boolean needWait;
Worker(Object object, boolean needWait)
{
this.object = object;
this.needWait = needWait;
}
@Override
public void run()
{
synchronized (object)
{
try {
if(needWait)
{
object.wait();
System.out.println(this.getName() + " 被唤醒了!");
}
else
{
//唤醒所有线程
object.notifyAll();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
Object ob = new Object();
for(int i = 0; i < 5; i ++)
{
Worker w = new Worker(ob, true);
w.setName("Thread-" + i);
w.start();
}
Worker w = new Worker(ob, false);
w.start();
Thread.sleep(2000);
}
}