多进程与多线程的区别?
本质的区别在于每个进程拥有自己的一整套变量,而线程则是共享数据。
线程的状态
- New(新建)
- Runnable(可运行)
- Blocked(阻塞)
- Waiting(等待)
- Timed waiting(计时等待)
- Terminated(终止)
确定一个线程的当前状态,只需要调用getState方法。当用new关键字创建一个新线程时,如new Thread(r),这个线程还没有开始运行。那意味着它的状态是新建(new)。当一个线程处于新建状态的时候,程序还没有开始运行线程中的代码。一旦调用start方法,线程就处于可运行(runnable)状态。这个线程具体什么时间开始运行,这取决于操作系统的的调用而不是代码的顺序。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完时操作系统剥夺该线程的运行权,并给另一个线程一个机会来运行。当选择下一个线程时,操作系统会考虑线程的优先级。
竞态条件
如上所述,线程间的数据是共享的,这也就引发了线程不安全的问题。有两个线程同时执行指令
这可能被拆分成三条机器指令
1、将account[to] 加载到寄存器。
2、增加account。
3、将结果写回account[to]
现在假设第一个线程执行完步骤1、2然后运行权被抢占。第二个线程执行完了3个步骤,此时线程一被唤醒,此时线程一将继续执行步骤3,即抹去线程二所做的更新。
锁对象
为了防止线程不安全,最简单的方式就是给该资源加锁。为达到这一目的可以使用synchronized,或者JUC的ReetrantLock。ReetrantLock保护代码块的基本结构如下:
1 2 3 4 5 6 7 8 9
| Lock lock = new ReentrantLock(); lock.lock(); try{ critical section } finally { lock.unlock(); }
|
这里要注意一定要把unlock操作放到finally子句中,因为一旦临界区抛出异常,锁必须释放,否则,其他线程将永远无法访问该资源。此外在try块中做判断时不允许使用if,必须使用while因为if是单次判断,假设if块中的代码被阻塞,然后唤醒,会直接向下执行而不是再次做判断,这会导致有些线程虽然被唤醒但此时并不满足if条件,但代码仍会继续执行。为了防止这种情况就必须使用while判断。
wait()、sleep()区别
wait()会放弃锁,而sleep()不会。
Callable与Runnable
获得线程的方式:
- 继承thread类
- 实现runnable接口
- 实现callable接口
- 通过线程池获得
Callable与Runnable的区别
1 2 3 4 5 6 7 8 9 10 11 12 13
| class MyThread implements Runnable{ @Override public void run() { } }
class MyThread2 implements Callable<Integer>{ @Override public Integer call() throws Exception { return 200; } }
|
- 是否有返回值
- 是否抛异常
- 落地方法不一样,一个是run,一个是call
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| import java.util.concurrent.Callable; import java.util.concurrent.FutureTask;
class MyThread implements Callable<Integer>{
@Override public Integer call() throws Exception { System.out.println("_______MyThread"); return 1024; } }
public class CallableDemo { public static void main(String[] args) throws Exception{ FutureTask futureTask = new FutureTask(new MyThread()); new Thread(futureTask,"A").start(); System.out.println(futureTask.get()); } }
|
控制线程调度顺序
CountDownLatch
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import java.util.concurrent.CountDownLatch;
public class CountDownDemo { public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(6); for (int i = 1; i <= 6; i++) { new Thread(() -> { System.out.println(Thread.currentThread().getName()); countDownLatch.countDown(); }, String.valueOf(i)).start(); } countDownLatch.await(); System.out.println(Thread.currentThread().getName() + "Main"); } }
|
CyclicBarrier
CountDownLatch是做减法,CyclicBarrier是做加法public CyclicBarrier(int parties, Runnable barrierAction)
中的parties
是临界点,阻塞的线程数量到达临界点的时候执行barrierAction
定义的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierDemo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(7,() -> { System.out.println("******"); }); for (int i = 1; i <= 7; i++) { final int tempInt = i; new Thread(() -> { System.out.println(Thread.currentThread().getName()); try { cyclicBarrier.await(); } catch (InterruptedException e){ e.printStackTrace(); } catch (BrokenBarrierException e){ e.printStackTrace(); } },String.valueOf(i)).start(); } } }
|
信号量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit;
public class SemaphoreDemo { public static void main(String[] args) { Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 6; i++) { new Thread(() -> { try { semaphore.acquire(); System.out.println(Thread.currentThread().getName() + "\t得到资源"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"\t释放资源"); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); } }, String.valueOf(i)).start(); } } }
|
读写锁ReadWriteLock
一个更细粒度的锁,在使用synchronized时会把所有同步方法都锁住,也就是所有操作都变成串行了。而ReadWriteLock提供读锁和写锁,读时可以并发的读,写时再独占资源。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69
| import java.util.HashMap; import java.util.Map; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock;
class MyCache { private volatile Map<String, Object> map = new HashMap<>(); private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
public void put(String key, Object value) { readWriteLock.writeLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t写入数据" + key); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } map.put(key, value); System.out.println(Thread.currentThread().getName() + "\t写入完成"); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.writeLock().unlock(); } }
public void get(String key) { readWriteLock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + "\t读取数据"); try { TimeUnit.MILLISECONDS.sleep(300); } catch (InterruptedException e) { e.printStackTrace(); } Object result = map.get(key); System.out.println(Thread.currentThread().getName() + "\t读取完成" + result); } catch (Exception e) { e.printStackTrace(); } finally { readWriteLock.readLock().unlock(); } } }
public class ReadWriteLockDemo { public static void main(String[] args) { MyCache myCache = new MyCache(); for (int i = 1; i <= 5; i++) { final int tempInt = i; new Thread(() -> { myCache.put(tempInt + "", tempInt + ""); }, String.valueOf(i)).start(); }
for (int i = 1; i <= 5; i++) { final int tempInt = i; new Thread(() -> { myCache.get(tempInt + ""); }, String.valueOf(i)).start(); } } }
|