1、多线程的创建一共有三种方式,一是继承 Thread 类,二是实现 Runnable 接口,然后借助 Thread (Target) 构造方法进行多线程的实例化,都需要覆盖重写内部的 run() 方法,使用start()方法调用,这两种方式都是没有返回结果的。还有第三种,实现Callable<T> 接口,覆盖重写 call()方法,然后需要配合FutureTask<T>使用,这里的泛型T就是返回的类型。Future会阻塞线程哦.
2、 对于某个类被public static 修饰的变量,或者非私有的变量,多个线程之间会共享这个变量,不管这个类被实例化多少次,这个变量都只会初始化一次,之后要看是否有线程对其进行修改。
3、多线程测试可以在main方法或者在Junit测试中进行,需要注意的是,sleep()只能在main 方法中测试,在Junit的方法中子线程并不会休眠
4、多线程有两类,一类是同时运行多个线程,线程之间没有通信,这种情况下要注意内存不要溢出,另一类是多线程且线程之间有通信,比如共享变量或对象,有先后顺序之分,这种情况下不单要注意内存不要溢出,还要注意线程安全。
5、wait() 和 notify ()是 Object 的属性,不是Thread 的属性,Object 是静态变量时,可以完成线程之间的通信。具体来说,并发同步的情况下,Object wait 本线程(线程1)阻塞,同级线程(线程2)开始运行占有资源,线程2调用notify 并运行完毕线程2其余代码释放资源,被阻塞的线程1开始运行,需要配合 synchronized (object)使用。wait 阻塞时,本线程和其余线程无差异,调用wait的线程在再次获得临界区上并没有特殊权重。wait不会使本线程退出,notify不会使本线程立即退出,前者等待其他线程调用 notify 释放临界区后继续运行(在阻塞线程中等待),后者则在剩余代码运行结束后才正式释放临界区并结束线程。Object.wait()不可以多次调用,否则会造成死锁,这一点是区分重入锁的。
6、join() 会使指定线程运行完毕之后再运行之后的代码,这就好比这个多线程不再是异步了,而是和之后的代码成了串行关系,join是加入的意思,子线程调用 join() 方法,子线程加入到父线程中,同级线程不会影响。必须先start再join,而且在线程池中join是无效的。
7、yield() 会让线程主动让出资源,但是资源仍旧可能被重新给这个线程。
8、synchronized 是 Java 语言中一个重量级操作,因为 Java 的线程是映射到操作系统的原生线程纸上的,如果要阻塞或者唤醒一个线程,都需要操作系统从用户态转为核心态,而状态转换需要耗费很多的处理器时间。synchronized对实例对象,实例方法,静态方法加锁。其中实例对象不可以为不变对象,比如Integer就是不变对象。
9、stop()被废弃了,因为太野蛮了。可以用 Thread.currentThread().interrupt()-设置中断、Thread.currentThread().isInterrupted()-仅查询是否中断、Thread.interrupted()-查询是否中断且消除中断标志,设置中断并不会造成线程立即退出,而是需要有效的逻辑支持,interrupt 是线程中断通信的一种支持方式,一般而言线程究竟能否中断在于是否走出循环的代码片段,但是有些情况下,可以规定线程是否响应中断,这对于等待中的线程来说是一种“福音”。请参考本文第20条。
10、Thread.sleep(int ms) 和 Thread.currentThread.sleep(int ms)作用和效果是一样的。
11、suspend() 挂起 和 resume() 继续执行被废弃,可能会导致死锁,因为挂起的时候并没有释放临界区资源。但是可以使用阻塞工具类,LockSupport,.park()和.unpark()。
12、volatile 修饰静态变量,这样虚拟机会采取一些措施,保证这个变量的可见性等特点。(有序、可见、原子)。volatile 保持简单操作的原子性,复合操作则无法取代锁的作用(比如i++),再者,可以在虚拟机server模式下确保数据的可见性和有序性(虚拟机server模式优化后本会忽视变量的变化,加上volatile后子线程又可以看到主线程对变量的修改)。volatile只能确保一个线程修改后其他线程能够看到这个改动,但当两个线程同时修改一个数据时,却依然会产生冲突。
底层实现来说,一般的线程都会有一个线程副本,这个副本来源于主存,使用volatile修饰的变量,不会有线程副本这一说,从而是各个线程共享了一个主存变量,确保了可见性。对于使用synchronized修饰的获取变量的方法而言,本地副本还是存在的,区别是,修改本地线程副本后,主存的变量会更新,即二者是同步的,其他线程调用本地副本前,会先同步主存中的变量。
13、synchronized 和 ReentrantLock 都用于解决线程同步的问题,synchronized 用于修饰一个对象、一个方法,ReentrantLock 修饰一个代码块,前者支持线程之间的通信,使用 wait和notify方法,后者也支持需要Condition 的支持。关键字 synchronized 的用法有多种,分为:指定对象加锁,直接作用于实例方法-相当于对当前实例加锁,直接作用于静态方法-对当前类枷锁。被synchronized 修饰的多个线程之间是串行关系。
14、ThreadLocal并不能替代同步机制,两者面向的问题领域不同。同步机制是为了同步多个线程对相同资源的并发访问,是为了多个线程之间进行通信的有效方式;而ThreadLocal是隔离多个线程的数据共享,从根本上就不在多个线程之间共享资源(变量),这样当然不需要对多个线程进行同步了。所以,如果你需要进行多个线程之间进行通信,则使用同步机制;如果需要隔离多个线程之间的共享冲突,可以使用ThreadLocal,这将极大地简化你的程序,使程序更加易读、简洁。
15、多线程中,线程池.shutdown()会下发线程池关闭命令,线程池.isShutdown会立即为true,线程池.isTerminated 会在线程池中所有任务结束之后返回true,否则为false。
即后面二者返回true.运行shutdown命令是前提。
16、ThreadGroup 为线程组,可以打包管理多个线程,线程组可以包含线程组,已经不推荐使用。
17、守护线程是一种特殊的线程,比如垃圾回收线程、JIT(动态编译,Just-In-Time,http://blog.csdn.net/hsuxu/article/details/9320699)线程,与守护线程对应的是用户线程,用户线程可以认为是系统的工作线程,它会完成这个程序应该要完成的业务操作。 当用户线程全部运行结束,守护线程才会停止。用户线程通过 setDaemon(true) 可以手动设置守护线程。守护线程区别于用户线程,用户线程可以指定为守护线程,需在start前设置,setDaemin(true),当用户线程结束后,守护线程会自动退出,尽管守护线程一般是死循环。
18、线程优先级级别高的优先运行,setPriority(int)。
19、ArrayList 是线程不安全的,需要采取同步措施。可以使用线程安全的 Vector 代替或者 Collections.synchronizasList(new LinkedList<Object>);
ArrayList与Vector都有一个初始的容量大小,当存储进它们里面的元素的个数超过了容量时,就需要增加ArrayList与Vector的存储空间,Vector默认增长为原来两倍,而ArrayList的增长策略为增长为原来的1.5倍,默认为10。ArrayList与Vector都可以设置初始的空间大小,Vector还可以设置增长的空间大小,而ArrayList没有提供设置增长空间的方法。
LinkedList线程不安全,HashMap线程不安全,HashSet线程不安全,HashTable线程安全。
20、ReentrantLock有中断lockInterruptibky响应中断,Condition有awaitUnintwrruptibly,不响应中断,Semaphore 有acquireUninterruptibky 不响应中断,即wait,ReentrantLock默认不响应中断,Condition,Semaphore默认响应中断。ReentrantLock又叫重入锁,可以重复加锁,但是Object.wait()是不可以重复的,只可以一次,多次的话会造成死锁。
Condition来自ReentrantLock.newCondition,Condition的各项调用需要先获得ReentrantLock.lock,使用完毕后需要unlock。ReentrantLock(true)表示锁是公平的,公平锁会降低效率 。Condition是锁内的锁。
21、信号量结合线程池使用,类似于,线程池是个饭店,信号量是某道菜,客人可以很多,但是每道菜就是那种上菜节奏。
22、线程池并非越大越好,因为即使是线程之间的切换也是需要开销的。
23、在读多写少的场景,CopyOnWriteArrayList性能好于Vector。可以使用Collections.synchronizedList(new LinkedList<String>)封装线程不安全的得到线程安全的。ConcurrentLinkedQueue是高并发环境中性能最好的队列。
24、线程池:Executor 框架提供的各种类型的线程池。new*ThreadPool,newSingleThread*Executor。Fixed固定,Cached无限量,schedule可以延迟时间。返回ExecutorService或ScheduledExecutorService。ThreadPoolExecutor是底层实现的支持。这几种都有耗尽资源的风险,一是创建无限多进程,二是任务队列无限制。newCachedThreadPool多于线程空闲60秒后会被回收。
ExecutorService DIYThreadPool = new ThreadPoolExecutor(0,2,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
25、倒计时器,CountDownLatch,它可以让某一个线程等待直到倒计时结束,才开始运行。计数减少是countdownlatch.countDown完成的,countdiwnlatch.await会检查计数是否减少为零,此外会让主线程等待子线程,有join()的效果。循环栅栏有同样的效果,其中CyclicBarrier.await有检查的意思。调用同一个循环栅栏的await方法会再线程数满足总条件后继续运行,一个线程调用两次,就会进行两次计数,这两次是分在两个批次中的。当所有的程序运行结束之后,CyclicBarrier会执行一个方法,也是以线程方式作为入参在构造方法中初始化的。单独声明,间接调用计数。
26、读写锁,ReentrantReadWriteLock,获取读锁:Lock lock=readWriteLock.readLock();获取写锁:readWriteLick.writeLock();操作,lock.lock(),lock.unlock,注意在 finally 代码内 unlock 防止造成死锁
27、内部锁synchronized和重入锁ReentrantLock一次都只允许一个线程访问一个资源。而信号量(Semaphore)可以指定多个线程,同时访问某一个资源。通过信号量获得锁和重入锁获得锁没有本质的差别。Thread像是容器,运行Runnable,多个Thread被传入同一个Runnable对象,将共享这个Runnable内部的锁约束,对于线程池来说也是一样的。
28、线程 start() 只能调用一次,否则报错.
29、Java 中的锁 Java 中的锁https://www.cnblogs.com/hustzzl/p/9343797.html30、Mysql 中的锁 innodb 中的行锁与表锁
https://blog.csdn.net/luzhensmart/article/details/81675527