进程:进程(Process)是计算机中正在运行的程序的实例。换句话来说进程就是计算机正在运行的软件。
线程:线程(Thread)是操作系统能够进行运算调度的最小单位,是进程的实际运行单位。
并发:同一时刻多个指令在单个cpu上交替执行。
并行:同一时刻多个指令在多个cpu上同时执行。
class MyThread extends Thread {
public void run() {
// 线程执行的代码
}
}
// 创建并启动线程
MyThread thread = new MyThread();
thread.start();
2.实现Runnable接口
class MyRunnable implements Runnable {
public void run() {
// 线程执行的代码
}
}
// 创建Runnable实例
MyRunnable runnable = new MyRunnable();
// 创建并启动线程
Thread thread = new Thread(runnable);
thread.start();
3.实现Callable接口
class MyCallable implements Callable<String> {
public String call() throws Exception {
System.out.println("线程开始执行");
for (int i = 0; i < 5; i++) {
System.out.println("线程执行中:" + i);
Thread.sleep(1000); // 休眠1秒
}
System.out.println("线程执行完毕");
return "线程执行完毕";
}
}
// 创建Callable实例
MyCallable callable = new MyCallable();
// 创建FutureTask实例
FutureTask<String> futureTask = new FutureTask<>(callable);
// 创建并启动线程
Thread thread = new Thread(futureTask);
thread.start();
// 获取线程执行结果
String result = futureTask.get();
System.out.println("线程执行结果:" + result);
产生原因:多个线程操作同一个共享数据时,一个线程访问的共享 数据被其他线程修改了, 那么就发生了线程安全问题。
**解决方法:**使用synchronized,Lock锁。
买票问题
public class SaleTick extends Thread{
public static int tick=0;
@Override
public void run() {
while (true){
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (tick<100){
tick++;
System.out.println(getName()+"正在售卖第"+tick+"张票");
}else {
break;
}
}
}
}
public class Test {
public static void main(String[] args) {
SaleTick saleTick = new SaleTick();
SaleTick saleTick1 = new SaleTick();
SaleTick saleTick2 = new SaleTick();
saleTick.start();
saleTick1.start();
saleTick2.start();
}
}
上述代码如果使用三个线程分别卖票那么可能会照成两种情况,一种是可能导致重复的票,另一种是可能出现超出范围的票,从而引发线程安全问题。
public class SaleTick extends Thread{
public static int tick=0;
static Object object=new Object();
@Override
public void run() {
while (true){
synchronized (object){
try {
sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (tick<100){
tick++;
System.out.println(getName()+"正在售卖第"+tick+"张票");
}else {
break;
}
}
}
}
}
解决办法是把操作买票的这个过程变成同步的,只有一个线程卖完了票另一个线程才能卖票。使用同步代码块要注意 synchronized的地方,确保同步的地方就是会产生线程安全的位置,同时synchronized中锁的对象是同一个对象。当然也可以把同步代码块的方法抽出变成一个同步方法。
使用Lock锁
public class SaleTick extends Thread{
public static int tick=0;
static Lock lock=new ReentrantLock();
@Override
public void run() {
while (true){
lock.lock();
try {
sleep(100);
if (tick<100){
tick++;
System.out.println(getName()+"正在售卖第"+tick+"张票");
}else {
break;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}finally {
lock.unlock();
}
}
}
}
Lock锁的特点是他可以自己空锁的开启和释放,使用Lock锁的时候也要注意两个问题,一个是什么时候是否为同一个锁,一个是要记得释放锁,释放锁的过程可以放在finally语句中。
使用wait()和notify()方法实现的方式生产者消费者模型,首先需要的是要有一个缓存区来存放生产的产品。当然也可以用队列来实现生产者-消费者模型。
缓存区
public class Buffer {
private int[] data;
private int size;
private int count;
private int in;
private int out;
public Buffer(int size) {
this.data = new int[size];
this.size = size;
this.count = 0;
this.in = 0;
this.out = 0;
}
public synchronized void put(int value) throws InterruptedException {
while (count == size) {
wait();
}
data[in] = value;
in = (in + 1) % size;
count++;
notifyAll();
}
public synchronized int get() throws InterruptedException {
while (count == 0) {
wait();
}
int value = data[out];
out = (out + 1) % size;
count--;
notifyAll();
return value;
}
}
生产者
public class Producer implements Runnable {
private Buffer buffer;
public Producer(Buffer buffer) {
this.buffer = buffer;
}
public void run() {
try {
for (int i = 0; i < 10; i++) {
buffer.put(i);
System.out.println("Produced " + i);
Thread.sleep((int) (Math.random() * 100));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
消费者
public class Consumer implements Runnable {
private Buffer buffer;
public Consumer(Buffer buffer) {
this.buffer = buffer;
}
public void run() {
try {
for (int i = 0; i < 10; i++) {
int value = buffer.get();
System.out.println("Consumed " + value);
Thread.sleep((int) (Math.random() * 100));
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试代码
public class Main {
public static void main(String[] args) {
Buffer buffer = new Buffer(5);
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
new Thread(producer).start();
new Thread(consumer).start();
}
}
上面的代码中,我们创建了一个大小为5的缓冲区,并分别启动了一个生产者和一个消费者线程。生产者会向缓冲区中添加10个数据,消费者会从缓冲区中取出10个数据。由于缓冲区的大小为5,因此生产者和消费者需要交替执行,以避免缓冲区溢出或空出。