JAVA学习笔记09
1. jar包
archive:归档
jar archive:jar包的归档
jar包—java压缩包
1. 方便项目的携带;
2. 方便于使用,只要在classpath路径设置jar路径基础;
3. 数据库驱动,SSH框架等都以jar包体现;
在cmd下把类打包成Jar包的第一种方式(将当前目录下的所有类文件打包到jar包中):
在cmd下把类打包成Jar包的第二种方式(将某个目录中的所有文件打包到jar中):
查看清单(jar -tf xxx.jar )并运行jar包文件命令(jar -cp xxx.jar xx.xx.xx.Xxxx<—完整类名):
指定清单文件(xxx.jar/META-INF/MENIFEST.MF)
jar [ctxui] [vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files…
在jar中指定函数的入口点之后,在随后运行jar内的类的时候可以省略一步:
最终完整的步骤为:
相对路径
1. .\ 当前文件夹下的文件
2. ..\当前文件夹的上一级文件的文件
3. cd . 当前文件夹
4. cd ..当前文件夹的上一级文件夹
2. 线程
- 进程:运行着的应用程序,进程间不能共享内存,因此通讯比较麻烦,一般需要套接字(socket)编程或者第三方软件通讯。
- 线程:在同一个应用程序内部并发执行的代码段,可以共享内存。
Java–>Thread线程类<—-java.lang.Thread
写线程必须继承Thread类并重写其中的run方法
其实栈也是一个线程(运行时概念)由方法帧组成,程序中main函数所在的线程为主线程
所谓的多线程就是同时开启了多个栈,在启动主栈时,由主栈开启了若干个分线程
示例代码:
package Java1;
public class firstThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread t1 = new MyThread();//在堆区
YourThread t2 = new YourThread()
t1.start();//在开启线程--->在分线程执行run函数
t2.start();
}
}
class MyThread extends Thread{
public void run() {//分线程执行的
while(true) {
System.out.println("MyThread()");
}
}
}
class YourThread extends Thread{
public void run() {//分线程执行的
while(true) {
System.out.println("YourThread()");
}
}
}
效果:
上面子线程无限循环,主线程永远关闭不了。
注:只有在所有的子线程关闭之后,主线程才能关闭。
yield();//在执行完上一步动作之后,执行该代码的瞬时放弃CPU的使用权,但是执行完毕之后立刻开抢。
观察以下代码:
package Java1;
public class firstThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread t1 = new MyThread();//在堆区
YourThread t2 = new YourThread();
t1.run();//在开启线程--->在分线程执行run函数
t2.start();
}
}
class MyThread extends Thread{
public void run() {
while(true) {
System.out.println("MyThread()");
}
}
}
class YourThread extends Thread{
public void run() {
while(true) {
System.out.println("YourThread()");
}
}
}
运行结果:
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
MyThread()
**
这个只有一个主线程,t1没有开启,t1.run()只是一个主线程中简单的函数调用。因为t1.run()是一个无线循环的方法,所以t2.start()一直无法执行,即t2的线程无法开启,因此只有一个主线程。**
Join()方法和sleep()方法示例
package Java1;
public class firstThread {
public static void main(String[] args) {
Player p1 = new Player("张三", 5000);
Player p2 = new Player("李四", 8000);
Player p3 = new Player("王五", 2000);
Player p4 = new Player("钱六", 3000);
p1.start();
p2.start();
p3.start();
p4.start();
//为了使一个线程执行完之后下一个线程才能执行
try {
p1.join();//子线程执行时,主线程暂时放弃CPU使用权,
//子线程执行完毕主线程才有可能获得CPU 使用权,如果不引入join方法,则主线程
//在获得CPU使用权时可能会直接执行System.out.println("人都到齐了,开始玩吧!");
p2.join();
p3.join();
p4.join();
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("人都到齐了,开始玩吧!");
}
}
class Player extends Thread{
private String name;
private int time;
public Player(String name, int time) {
this.name = name;
this.time = time;
}
public void run() {
System.out.println("玩家:"+name+"出发了!");
try {
Thread.sleep(time);
} catch (Exception e) {
}
System.out.println("玩家花费"+time+"毫秒到了。");
}
}
输出:
玩家:王五出发了!
玩家:钱六出发了!
玩家:李四出发了!
玩家:张三出发了!
玩家花费2000毫秒到了。
玩家花费3000毫秒到了。
玩家花费5000毫秒到了。
玩家花费8000毫秒到了。
人都到齐了,开始玩吧!
观察下面的程序和上面有什么不同
package Java1;
public class firstThread {
public static void main(String[] args) {
Player p1 = new Player("张三", 5000);
Player p2 = new Player("李四", 8000);
Player p3 = new Player("王五", 2000);
Player p4 = new Player("钱六", 3000);
try {
p1.start();
p1.join();//p1开启线程,p1线程执行完毕之后,下一步动作
p2.start();
p2.join();//p2开启线程,p2线程执行完毕之后,下一步动作
p3.start();
p3.join();//p3开启线程,p3线程执行完毕之后,下一步动作
p4.start();
p4.join();//p4开启线程,p4线程执行完毕之后,下一步动作
} catch (Exception e) {
// TODO: handle exception
}
System.out.println("人都到齐了,开始玩吧!");
}
}
class Player extends Thread{
private String name;
private int time;
public Player(String name, int time) {
this.name = name;
this.time = time;
}
public void run() {
System.out.println("玩家:"+name+"出发了!");
try {
Thread.sleep(time);
} catch (Exception e) {
}
System.out.println("玩家花费"+time+"毫秒到了。");
}
}
输出:
玩家:张三出发了!
玩家花费5000毫秒到了。
玩家:李四出发了!
玩家花费8000毫秒到了。
玩家:王五出发了!
玩家花费2000毫秒到了。
玩家:钱六出发了!
玩家花费3000毫秒到了。
人都到齐了,开始玩吧!
就像将并行的线程串行起来运行一样,用多线程实现单线程了,很尴尬。
守护线程daemon的概念
如果一个线程是守护线程或者程序中剩余的线程都是守护线程,那就不再运行了,程序结束。
例子:你KTV唱歌的时候,服务员会每隔十分钟给你播报一下时间,但是唱歌到点要走了,再播报时间就没意义了,那播报时间就是守护线程,唱完的时候应该只剩下守护线程了,该线程应该结束。
考虑如下示例–没有守护线程:
package Java1;
public class firstThread {
public static void main(String[] args) {
KtvRoom k1 = new KtvRoom(1, 15000);
KtvWaiter k2 = new KtvWaiter();
k1.start();
k2.start();
}
}
class KtvRoom extends Thread{
private int number;
private int time;
public KtvRoom(int number, int time) {
this.number = number;
this.time = time;
}
public void run() {
System.out.println(number+"号KTV包厢正在唱歌!");
try {//这种异常时人家方法里面规定的,不抛你编译都通不过。
Thread.sleep(time);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(number+"号KTV包厢的人已经唱完,准备走人!");
}
}
class KtvWaiter extends Thread{
public void run() {
while(true) {
System.out.println(new java.util.Date());//获取程序当前时间
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
运行结果:
1号KTV包厢正在唱歌!
Thu Apr 05 22:16:14 CST 2018
Thu Apr 05 22:16:15 CST 2018
Thu Apr 05 22:16:16 CST 2018
Thu Apr 05 22:16:17 CST 2018
Thu Apr 05 22:16:18 CST 2018
Thu Apr 05 22:16:19 CST 2018
Thu Apr 05 22:16:20 CST 2018
Thu Apr 05 22:16:21 CST 2018
Thu Apr 05 22:16:22 CST 2018
Thu Apr 05 22:16:23 CST 2018
Thu Apr 05 22:16:24 CST 2018
Thu Apr 05 22:16:25 CST 2018
Thu Apr 05 22:16:26 CST 2018
Thu Apr 05 22:16:27 CST 2018
Thu Apr 05 22:16:28 CST 2018
1号KTV包厢的人已经唱完,准备走人!
Thu Apr 05 22:16:29 CST 2018
Thu Apr 05 22:16:30 CST 2018
Thu Apr 05 22:16:31 CST 2018
Thu Apr 05 22:16:32 CST 2018
Thu Apr 05 22:16:33 CST 2018
唱完都走了还在播报显然是不合理的。
而加入守护线程之后
package Java1;
public class firstThread {
public static void main(String[] args) {
KtvRoom k1 = new KtvRoom(1, 15000);
KtvWaiter k2 = new KtvWaiter();
//设置服务员为守护线程
k2.setDaemon(true);
k1.start();
k2.start();
}
}
class KtvRoom extends Thread{
private int number;
private int time;
public KtvRoom(int number, int time) {
this.number = number;
this.time = time;
}
public void run() {
System.out.println(number+"号KTV包厢正在唱歌!");
try {
Thread.sleep(time);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(number+"号KTV包厢的人已经唱完,准备走人!");
}
}
class KtvWaiter extends Thread{
public void run() {
while(true) {
System.out.println(new java.util.Date());//获取程序当前时间
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
运行结果:
1号KTV包厢正在唱歌!
Thu Apr 05 22:22:52 CST 2018
Thu Apr 05 22:22:53 CST 2018
Thu Apr 05 22:22:54 CST 2018
Thu Apr 05 22:22:55 CST 2018
Thu Apr 05 22:22:56 CST 2018
Thu Apr 05 22:22:57 CST 2018
Thu Apr 05 22:22:58 CST 2018
Thu Apr 05 22:22:59 CST 2018
Thu Apr 05 22:23:00 CST 2018
Thu Apr 05 22:23:01 CST 2018
Thu Apr 05 22:23:02 CST 2018
Thu Apr 05 22:23:03 CST 2018
Thu Apr 05 22:23:04 CST 2018
Thu Apr 05 22:23:05 CST 2018
Thu Apr 05 22:23:06 CST 2018
1号KTV包厢的人已经唱完,准备走人!
唱完歌守护线程就结束了!!!
守护线程守护的是所有的非守护线程。
当然也可以在类里面的构造函数直接定义,示例如下:
package Java1;
public class firstThread {
public static void main(String[] args) {
KtvRoom k1 = new KtvRoom(1, 15000);
KtvWaiter k2 = new KtvWaiter();
k1.start();
k2.start();
}
}
class KtvRoom extends Thread{
private int number;
private int time;
public KtvRoom(int number, int time) {
this.number = number;
this.time = time;
}
public void run() {
System.out.println(number+"号KTV包厢正在唱歌!");
try {
Thread.sleep(time);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(number+"号KTV包厢的人已经唱完,准备走人!");
}
}
class KtvWaiter extends Thread{
public KtvWaiter(){
//设置服务员守护线程------------------------------->看这里!!!!!
this.setDaemon(true);
}
public void run() {
while(true) {
System.out.println(new java.util.Date());//获取程序当前时间
try {
Thread.sleep(1000);
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
运行结果和上面一个样子的。
多线程在并发的时候使用
在多线程编程创建对象的时候堆是共享的(创建对象全在堆里),而栈是独占的。
3. 锁:一种独占的行为
任意一个对象都是锁。信号灯,参照物。
以两个人卖10张票为例:
示例1
package Java1;
public class firstThread {
public static void main(String[] args) {
Salor s1 = new Salor("张三");
Salor s2 = new Salor("李四");
s1.start();
s2.start();
}
}
class Salor extends Thread{
//定义两个售票员一共要卖的票数
static int tickets = 10;
private String name;
int currentTickets = 0;
public Salor(String name) {
this.name = name;
}
public void run() {
while(tickets>0) {
currentTickets=tickets;
System.out.println(name+": "+tickets);
tickets = tickets -1;
}
}
}
输出:
张三: 10
李四: 10
张三: 9
李四: 8
张三: 7
李四: 6
张三: 5
李四: 4
李四: 2
李四: 1
张三: 3
分析:两个10,两个线程同时跑,在run开始的时候很有可能两个线程同时到达while循环中的第一条语句,所以可能都输出10。
示例2:
package Java1;
public class firstThread {
public static void main(String[] args) {
Salor s1 = new Salor("张三");
Salor s2 = new Salor("李四");
s1.start();
s2.start();
}
}
class Salor extends Thread{
//定义锁可以用任意类名定义这个锁比如String object....因为多线程共享一个锁所以要用静态的
static Object lock = new Object();
//定义两个售票员一共要卖的票数
static int tickets = 10;
private String name;
public Salor(String name) {
this.name = name;
}
public void run() {
int currentTickets =0 ;
while(tickets > 0) {
synchronized(lock) {
currentTickets=tickets;
tickets = tickets -1;
System.out.println(name+":"+currentTickets);
}
}
}
}
运行结果:
李四:7
李四:6
李四:5
李四:4
李四:3
李四:2
李四:1
张三:0
问题:出现了0,因为在李四卖最后一张票的时候张三已经进入while并在锁的前面等着了,而此时李四正在锁里面执行语句tickets=tickets-1=0,所以当李四的买票结束,张三的tickets为0,输出张三卖了第0张票
为了克服这个问题,尝试把while也放进锁里面,改进如下(示例3):
package Java1;
public class firstThread {
public static void main(String[] args) {
Salor s1 = new Salor("张三");
Salor s2 = new Salor("李四");
s1.start();
s2.start();
}
}
class Salor extends Thread{
//定义锁可以用任意类名定义这个锁比如String object....因为多线程共享一个锁所以要用静态的
static Object lock = new Object();
//定义两个售票员一共要卖的票数
static int tickets = 10;
private String name;
public Salor(String name) {
this.name = name;
}
public void run() {
int currentTickets =0 ;
synchronized (lock) {
while(tickets > 0) {
currentTickets=tickets;
tickets = tickets -1;
System.out.println(name+":"+currentTickets);
}
}
}
}
输出:
张三:10
张三:9
张三:8
张三:7
张三:6
张三:5
张三:4
张三:3
张三:2
张三:1
**问题:结果全是张三卖的
分析:因为while在锁里面,当s1线程运行run的时候,先上锁,然后直接在锁里面的while中一直循环,而李四的线程根本就进不去,所以李四的线程根本就没有执行,直到锁里面的张三把所有票都卖完了,李四才能进入锁里面,但是都没票了还卖个锤子。所以只有张三卖票。
**
继续改进:主旨思想–>只将关键动作执行同步代码块(示例4:)
package Java1;
public class firstThread {
public static void main(String[] args) {
Salor s1 = new Salor("张三");
Salor s2 = new Salor("李四");
Salor s3 = new Salor("王五");
s1.start();
s2.start();
s3.start();
}
}
class Salor extends Thread{
//定义锁可以用任意类名定义这个锁比如String object....因为多线程共享一个锁所以要用静态的
static Object lock = new Object();
//定义两个售票员一共要卖的票数
static int tickets = 10;
private String name;
public Salor(String name) {
this.name = name;
}
public void run() {
while(true) {
int tick = getTicket();
if (tick > 0) {
System.out.println(name+": "+tick);
}else {
return;
}
}
}
public int getTicket() {//同步代码块,同一时刻只有一个线程会运行
synchronized (lock) {
int curTickets = tickets;
tickets = tickets - 1;
return curTickets;
}
}
}
结果:
张三: 9
王五: 8
李四: 10
王五: 6
张三: 7
王五: 4
李四: 5
王五: 2
张三: 3
李四: 1
总结:锁为什么要定义成成静态的–>静态方法与对象无关,只与类有关。
如果我不想定义静态的锁呢—->索性连锁都不要了,直接上静态同步方法(示例5)
package Java1;
public class firstThread {
public static void main(String[] args) {
Salor s1 = new Salor("张三");
Salor s2 = new Salor("李四");
Salor s3 = new Salor("王五");
s1.start();
s2.start();
s3.start();
}
}
class Salor extends Thread{
//定义两个售票员一共要卖的票数
static int tickets = 10;
private String name;
public Salor(String name) {
this.name = name;
}
public void run() {
while(true) {
int tick = getTicket();
if (tick > 0) {
System.out.println(name+": "+tick);
}else {
return;
}
}
}
public static synchronized int getTicket() {//同步代码块,同一时刻只有一个线程会运行
int curTickets = tickets;
tickets = tickets - 1;
return curTickets;
}
}
输出:
张三: 10
王五: 8
李四: 9
王五: 6
张三: 7
王五: 4
李四: 5
李四: 1
王五: 2
张三: 3
同步方法是以对象自身为锁(印证上面所说上面所有对象都是锁),然后通过将对象锁定义为静态锁,使多个对象锁合并为一个公用锁—即把类作为锁(类的实例化–对象,对象的抽象–类),然后运行多线程。
锁操作一般都是面向对象的。
进一步如果票池(ticket = 10)我都不想定义成静态的呢—可以直接定义成一个对象加以操作,即整个过程都是对象的操作(示例6)
package Java1;
import java.util.jar.Attributes.Name;
public class firstThread {
public static void main(String[] args) {
//这就是全部面向对象的同步方法
TicketPool pool = new TicketPool();
Salor s1 = new Salor("张三", pool);
Salor s2 = new Salor("李四", pool);
Salor s3 = new Salor("王五", pool);
s1.start();
s2.start();
s3.start();
}
}
class Salor extends Thread{
private String name;
TicketPool ticketPool;
public Salor(String name, TicketPool pool) {
this.name = name;
this.ticketPool = pool;
}
public void run() {
while(true) {
int tick = ticketPool.getTicket();
if (tick > 0) {
System.out.println(name+": "+tick);
}else {
return;
}
}
}
}
class TicketPool{
//定义票池票的数目
private int ticketNum = 10;
public synchronized int getTicket() {//定义取票这个动作
int ticket = ticketNum;
ticketNum = ticketNum - 1;
return ticket;
}
}
结果:
张三: 10
王五: 6
李四: 7
王五: 4
张三: 5
张三: 1
王五: 2
李四: 3
练习
练习1的代码–同步代码块(没有答案,自己参考网上写的)
package Java1;
public class guosandong {
public static void main(String[] args) {
// TODO Auto-generated method stub
String carveName = "秦岭隧道";
Car car1 = new Car(10, carveName, "奔驰");
Car car2 = new Car(5, carveName, "现代");
Car car3 = new Car(3, carveName, "沃尔沃");
Car car4 = new Car(2, carveName, "五菱通用");
car1.start();
car2.start();
car3.start();
car4.start();
}
}
class Car extends Thread{
private int time;
private String carveName;
private String carName;
private static Object lock = new Object();
public Car(int time,String carveName, String carName) {
this.carName = carName;
this.time = time;
this.carveName = carveName;
}
public void run() {
synchronized(lock) {
System.out.println(carName+"已经开始进入"+carveName+"。");
try {
Thread.sleep(time*1000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(carName+"已经通过了"+carveName+"隧道,用时"+time+"秒。");
}
}
}
结果:
奔驰已经开始进入秦岭隧道。
奔驰已经通过了秦岭隧道隧道,用时10秒。
五菱通用已经开始进入秦岭隧道。
五菱通用已经通过了秦岭隧道隧道,用时2秒。
沃尔沃已经开始进入秦岭隧道。
沃尔沃已经通过了秦岭隧道隧道,用时3秒。
现代已经开始进入秦岭隧道。
现代已经通过了秦岭隧道隧道,用时5秒。
同步方法
package Java1;
public class guosandong {
public static void main(String[] args) {
// TODO Auto-generated method stub
Carve carve = new Carve();
Car car1 = new Car(10, carve, "奔驰");
Car car2 = new Car(5, carve, "现代");
Car car3 = new Car(3, carve, "沃尔沃");
Car car4 = new Car(2, carve, "五菱通用");
car1.start();
car2.start();
car3.start();
car4.start();
}
}
class Car extends Thread{
private int time;
private Carve carve;
private String carName;
public Car(int time,Carve carve, String carName) {
this.carName = carName;
this.carve = carve;
this.time = time;
}
public void run() {
carve.crossCarve(time,carName);
}
}
class Carve{
private String carveName = "秦岭隧道";
public synchronized void crossCarve(int time,String carName) {
System.out.println(carName+"已经开始进入"+carveName+"。");
try {
Thread.sleep(time*1000);
} catch (Exception e) {
// TODO: handle exception
}
System.out.println(carName+"已经通过了"+carveName+"隧道,用时"+time+"秒。");
}
}
结果:
奔驰已经开始进入秦岭隧道。
奔驰已经通过了秦岭隧道隧道,用时10秒。
五菱通用已经开始进入秦岭隧道。
五菱通用已经通过了秦岭隧道隧道,用时2秒。
沃尔沃已经开始进入秦岭隧道。
沃尔沃已经通过了秦岭隧道隧道,用时3秒。
现代已经开始进入秦岭隧道。
现代已经通过了秦岭隧道隧道,用时5秒。
练习2–同步代码块
package Java1;
public class guosandong {
public static void main(String[] args) {
for(int i =0 ;i<50;i++) {
new User("用户"+(i+1)).start();
}
}
}
class User extends Thread{
static Object lock = new Object();
static int userNum =1;
int curUser = 0;
String userName;
public User(String userName) {
this.userName = userName;
}
public void run() {
synchronized(lock) {
curUser = userNum;
userNum = userNum + 1;
}
System.out.println(userName+"取到的票号为:"+curUser+"。");
}
}
输出:
用户1取到的票号为:1。
用户2取到的票号为:2。
用户3取到的票号为:3。
用户4取到的票号为:4。
用户5取到的票号为:5。
用户6取到的票号为:6。
...
用户42取到的票号为:49。
用户46取到的票号为:46。
用户43取到的票号为:50。
同步方法
package Java1;
public class guosandong {
public static void main(String[] args) {
for(int i =0 ;i<50;i++) {
new User("用户"+(i+1)).start();
}
}
}
class User extends Thread{
static int userNum =1;
String currentUser;
int ticketNumber;
public User(String currentUser) {
this.currentUser = currentUser;
}
public void run() {
ticketNumber = getTicket();
System.out.println(currentUser+"取到的票号为:"+ticketNumber+"。");
}
public synchronized int getTicket() {
int curUser = userNum;
userNum = userNum + 1;
return curUser;
}
}
结果:
用户1取到的票号为:1。
用户2取到的票号为:2。
用户4取到的票号为:3。
用户5取到的票号为:4。
用户3取到的票号为:5。
用户6取到的票号为:6。
...
用户43取到的票号为:50。
用户44取到的票号为:49。